为什么不能使用抽象类型的具体实现来推断 ClassTag?

Why a concrete implementation of an abstract type cannot be used to infer ClassTag?

考虑以下代码:

object DelayedClassTagInference {

  trait SS {

    type TT <: Any

    implicit val ctg: ClassTag[TT] = implicitly[ClassTag[TT]]

    val fakeCtg: ClassTag[None.type] = implicitly[ClassTag[None.type]]

  }

  class Sub1 extends SS {

    override final type TT = Int
  }

  class Sub2 extends SS {

    override final type TT = Double
  }

  class Sub3 extends SS {

    override final type TT = String
  }
}

class DelayedClassTagInference extends FunSpec {

  import DelayedClassTagInference._

  it("") {

    val sub1 = new Sub1()
    println(sub1.fakeCtg)
    println(sub1.ctg)
  }
}

当Sub1 & Sub2初始化时,类型TT已经确定,所以ClassTag[Int]和ClassTag[Double]可以很容易的通过类型class规则推断出来。

不幸的是,当我运行上面的代码。我得到以下结果:

scala.None$
null

所以ctg的值为null,除了触发NullPointerException外,这也是没有意义的。是不是以后应该修复的scala包?

删除 val ctg 的修饰符 implicit,您会发现您的代码无法编译。您不应该手动定义隐式 ClassTag/TypeTag/WeakTypeTag,它们应该在类型已知时由编译器自动生成。

实际上,当您调用 implicitly[ClassTag[TT]] 时,会使用您现在定义的隐式 val ctg: ClassTag[TT],这就是它在运行时 null 的原因。

隐式在编译时解析,当您调用 sub1.ctg 时,解析调用哪个 .ctg 发生在运行时(这就是子类型多态性的工作原理)。在编译时还不知道它是 Sub1#ctg.


替换

implicit val ctg: ClassTag[TT] = implicitly[ClassTag[TT]] 

def ctg(implicit tag: ClassTag[TT]): ClassTag[TT] = implicitly[ClassTag[TT]] 

你将在运行时得到 Int 而不是 null