为什么我们必须显式指定ClassTag类型类

Arn*_*sen 5 scala typeclass scala-reflect

现在scala已经使用类型类迭代了JVM 类型的擦除修复ClassTag,为什么它是一个选择加入,而不是让编译器总是捕获运行时检查的类型签名.使它具有隐式参数化类型约束将使得classTag[T]无论泛型参数声明如何都可以调用.

编辑:我应该澄清,我并不是说scala应该改变幕后的签名,总是包含在内ClassTag.相反,我的意思是,因为ClassTag显示scala可以捕获运行时类型信息并因此避免类型擦除限制,为什么不能将该捕获隐含为编译器的一部分,以便该信息始终在scala代码中可用?

我怀疑它是向后兼容性,java生态系统兼容性,二进制大小或运行时开销相关,但这些只是猜测.

Mic*_*jac 3

向后兼容性真的会被彻底破坏。如果您有一个简单的方法,例如:

def foo[A](a: A)(implicit something: SomeType) = ???
Run Code Online (Sandbox Code Playgroud)

然后假设在Scala的下一个版本中,编译器突然在ClassTag所有带有类型参数的方法的签名中添加了implicit。这个方法就会被打破。任何被明确调用的地方都foo(a)(someTypeValue)不再起作用了。二进制和源代码兼容性将会消失。

Java 的互操作性会很糟糕。假设我们的方法现在如下所示:

def foo[A : ClassTag](a: A) = ???
Run Code Online (Sandbox Code Playgroud)

由于ClassTags 是由 Scala 编译器生成的,因此在 Java 中使用此方法会更加困难。你必须ClassTag自己创建。

ClassTag<MyClass> tag = scala.reflect.ClassTag$.MODULE$.apply(MyClass.class);
foo(a, tag);
Run Code Online (Sandbox Code Playgroud)

我的 Java 可能不是 100% 正确,但你明白了。任何参数化的东西都会变得非常丑陋。好吧,如果它需要隐式,那么就已经是这样了ClassTag,但是需要这样做的方法类将会急剧增加。

此外,在我们(至少我)使用的大多数参数化方法中,类型擦除并不是什么大问题。由于上述原因,我认为自动要求ClassTag每个类型参数会带来更多麻烦,而不是有帮助。

当然,这会增加更多的编译器开销,因为它需要生成ClassTag比通常更多的 s。我不认为它会增加更多的运行时开销,除非ClassTag有什么不同。例如,在如下所示的简单方法中,ClassTag实际上并没有执行任何操作:

def foo[A : ClassTag](a: A): A = a
Run Code Online (Sandbox Code Playgroud)

我们还应该注意到它们也不完美。因此,添加它们并不是擦除问题的最终解决方案。

val list = List(1, "abc", List(1, 2, 3), List("a", "b"))
def find[A: ClassTag](l: List[Any]): Option[A] =
    l collectFirst { case a: A => a }

scala> find[List[String]]
res2: Option[List[String]] = Some(List(1, 2, 3)) // Not quite! And no warnings, either.
Run Code Online (Sandbox Code Playgroud)

ClassTag向每个类实例添加 a会增加开销,并且肯定还会破坏兼容性。这在很多地方也是不可能的。我们不能只是注入java.lang.String一个ClassTag. 此外,我们仍然很容易被删除。在每个类中拥有一个ClassTag字段实际上并不比使用getClass. 我们可以进行比较

case a if(a.getClass == classOf[String]) => a.asInstanceOf[String]
Run Code Online (Sandbox Code Playgroud)

但这是非常丑陋的,需要演员阵容,而且不一定是ClassTag要解决的问题。如果我用我的方法尝试这样的事情find,它根本行不通。

// Can't compile
def find[A](l: List[Any]): Option[A] =
    l collectFirst { case a if(a.getClass == classOf[A]) => a.asInstanceOf[A] }
Run Code Online (Sandbox Code Playgroud)

即使我要设计这个以某种方式与 一起工作ClassTag,它会从哪里来?我不能a.classTag == classTag[A],因为A已经被抹掉了。我需要ClassTag在方法调用站点。