使用相同类型的参数作为参数类型和带有匹配表达式的参数类型

Using same type parameter as argument type and parameter type with match expression

我在编译以下示例代码时遇到错误。

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] 这样的实例化后面。但是,如果有很多匹配案例模式,那么对所有 return 值都这样做很烦人。

此外,我想使用 Transform class 作为父级 class 并覆盖 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)
  }
}

是否有更好的方法或技巧?

我建议改用 typeclass

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()
}

To avoid this, I need to put asInstanceOf[T] behind instantiation like A(i).asInstanceOf[T]. However, it is annoying to do like that for all return value if there are a lot of match case patterns.

嗯,这个问题很简单:把它放在比赛结束时的一个地方,而不是每个分支。

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

但请注意,您的设计本质上是不安全的,因为除了 BaseAB 之外还有 Base 的子类型:单例类型(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)
}