Использование того же типа параметра, что и тип аргумента и тип параметра с выражением соответствия - PullRequest
0 голосов
/ 30 июня 2019

Я получаю ошибки, компилируя следующий пример кода.

abstract class Base
case class A(i: Int)    extends Base
case class B(s: String) extends Base

class Transform {
  def func[T <: Base](arg: T): T = arg match {
    case A(i) => A(i)
    case B(s) => B(s)
  }
}

ошибки

Example.scala:9: error: type mismatch;
 found   : A
 required: T
    case A(i) => A(i)
                  ^
Example.scala:10: error: type mismatch;
 found   : B
 required: T
    case B(s) => B(s)
                  ^
two errors found

Эти ошибки являются разумными.
Чтобы избежать этого, мне нужно поставить asInstanceOf[T] за экземпляром как A(i).asInstanceOf[T].Тем не менее, раздражает делать это для всех возвращаемых значений, если имеется много шаблонов совпадений.

Кроме того, я хочу использовать класс Transform в качестве родительского класса и переопределить func() для выполненияконкретная операция, подобная приведенной ниже, код.

class ExtTransform extends Transform {
  override def func[T <: Base](arg: T): T = arg match {
    case A(i) => A(i + 1)
    case _    => super.func(arg)
  }
}

Есть ли лучшие способы или хитрость?

Ответы [ 2 ]

3 голосов
/ 30 июня 2019

Чтобы избежать этого, мне нужно поставить asInstanceOf [T] за экземпляром, как A (i) .asInstanceOf [T]. Тем не менее, это раздражает, если делать так для всего возвращаемого значения, если имеется много шаблонов совпадений.

Что ж, эта проблема проста: поместите ее в одном месте в конце матча вместо каждой ветви.

override def func[T <: Base](arg: T): T = (arg match {
  case A(i) => A(i)
  case B(s) => B(s)
}).asInstanceOf[T]

Но учтите, что ваш дизайн по своей сути небезопасен, поскольку существуют подтипы Base, отличные от Base, A и B: одиночные типы (a.type), составные типы (A with SomeTrait), Null ... и любой из них можно использовать как T. Может быть лучше просто иметь перегрузки:

class Transform {
  def func(arg: Base): Base = arg match {
    case arg: A => func(arg)
    case arg: B => func(arg)
  }

  def func(arg: A): A = arg
  def func(arg: B): B = arg
}

class ExtTransform extends Transform {
  override def func(arg: A): A = A(arg.i + 1)
}
0 голосов
/ 30 июня 2019

Я бы предложил вместо этого использовать класс типов .

sealed trait Base

object Base {
  final case class A() extends Base
  final case class B() extends Base

  sealed trait Builder[T <: Base] {
     def build(): T
  }

  object Builder {
     final implicit val ABuilder: Builder[A] = new Builder[A] {
       override def build(): A = A()
     }

     final implicit val BBuilder: Builder[B] = new Builder[B] {
       override def build(): B = B()
     }
  }
}

object Main extends App {
  def func[T <: Base](implicit builder: Base.Builder[T]): T =
    builder.build()

  func[Base.A] // res: Base.A = A()
  func[Base.B] // res: Base.B = B()
}
...