提供子类实例代替超类时出现 Scala 错误?

Scala error when providing subclass instance in place of superclass?

我只是在 scala 中尝试一些东西,我写了这段代码

object Main:
  def main(args: Array[String]): Unit =
    val dInt: Data[Int] = IntData(1)
    val dString: Data[String] = StringData("hello")
    val Data(deconstructedInt) = dInt // unapply
    val Data(deconstructedString) = dString // unapply
    println(deconstructedInt)
    println(deconstructedString)


sealed trait Data[+T]:
  def get: T

case class IntData(get: Int) extends Data[Int]

case class StringData(get: String) extends Data[String]

object Data:
  def apply[T](input: T): Data[T] = input match {
    case i: Int => IntData(i) //compile error here
    case s: String => StringData(s) //compile error here
  }

  def unapply[T](d: Data[T]): Option[String] = d match {
    case IntData(get) => Some(s"int data => get = $get")
    case StringData(get) => Some(s"string data => get = $get")
  }

我在代码注释的位置得到这个错误

Found:    IntData
Required: Data[T]
    case i: Int => IntData(i)

为什么我会收到此错误,IntData(或 StringData)不是 Data 的子类吗?

IntDataData[Int] 的子类型。因此,如果 T 不是 Int,那么 IntData 而不是 Data[T] 的子类型。现在,您可能会说,如果 input 匹配第一种情况,那么显然 TInt。但是编译器并不聪明地意识到这一点!

您可以尝试使用 Scala 3 的新匹配类型:

type DataOf[T] = T match {
  case Int => IntData
  case String => StringData
}

def apply[T](input: T): DataOf[T] = input match {
  case i: Int => IntData(i)
  case s: String => StringData(s)
}

另一种选择是联合类型:

def apply(input: Int | String): Data[Int | String] = input match {
  case i: Int => IntData(i)
  case s: String => StringData(s)
}

但是,这样做会丢失类型信息。使用匹配类型解决方案,apply(42) 被推断为具有类型 IntData。使用联合类型解决方案,推断类型为 Data[Int | String].

编译器通过这种方式连接 T 之间的点并且它有效:

  object Main:
    def main(args: Array[String]): Unit =
      val dInt: Data[Int] = IntData(1)
      val dString: Data[String] = StringData("hello")
      val Data(deconstructedInt) = dInt // unapply
      val Data(deconstructedString) = dString // unapply
      println(deconstructedInt)
      println(deconstructedString)

  sealed trait Data[+T]:
    def get: T

  case class IntData[T <: Int](get: T) extends Data[T]

  case class StringData[T <: String](get: T) extends Data[T]

  object Data:
    def apply[T](input: T): Data[T] = input match {
      case i: Int    => IntData(i)
      case s: String => StringData(s)
    }

    def unapply[T](d: Data[T]): Option[String] = d match {
      case IntData(get)    => Some(s"int data => get = $get")
      case StringData(get) => Some(s"string data => get = $get")
    }