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

时间:2015-05-17 23:04:57

标签: scala typeclass scala-reflect

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

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

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

2 个答案:

答案 0 :(得分:3)

向后兼容性会被完全破坏。如果你有一个简单的方法,如:

def foo[A](a: A)(implicit something: SomeType) = ???

然后假设在下一版本的Scala中,编译器突然将隐式ClassTag添加到具有类型参数的所有方法的签名中。这种方法会被打破。它被明确称为foo(a)(someTypeValue)的任何地方都不再适用。二进制和源兼容性将消失。

Java互操作性会很难看。假设我们的方法现在看起来像这样:

def foo[A : ClassTag](a: A) = ???

因为ClassTag是由Scala编译器生成的,所以使用Java中的这种方法会更加困难。您必须自己创建ClassTag

ClassTag<MyClass> tag = scala.reflect.ClassTag$.MODULE$.apply(MyClass.class);
foo(a, tag);

我的Java可能不是100%正确,但你明白了。任何参数化的东西都会变得非常难看。好吧,它已经是需要隐式ClassTag,但是必要的方法类会大大增加。

此外,类型擦除并不是我们(至少)使用的大多数参数化方法中的大部分问题。我认为由于上述原因,自动要求每个类型参数ClassTag会比它有所帮助的麻烦要多得多。

当然这会增加更多的编译器开销,因为它需要生成比通常更多的ClassTag s。除非ClassTag产生影响,否则我不会认为会增加更多的运行时间开销。例如,在下面的简单方法中,ClassTag并没有真正做任何事情:

def foo[A : ClassTag](a: A): A = a

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

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.

向每个单独的类实例添加ClassTag会增加开销,并且肯定也会破坏兼容性。它在很多地方也是不可能的。我们不能仅使用java.lang.String注入ClassTag。此外,我们仍然容易被擦除。在每个类中使用ClassTag字段实际上并不比使用getClass更好。我们可以进行比较,如

case a if(a.getClass == classOf[String]) => a.asInstanceOf[String]

但这非常丑陋,需要演员表,并不一定是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] }

即使我想以某种方式与ClassTag合作,它会来自哪里?我无法a.classTag == classTag[A],因为A已被删除。我需要方法调用网站上的ClassTag

答案 1 :(得分:2)

是的,对于很少使用的用例(例如需要阵列构造或异构映射值恢复),您将惩罚任何通用方法或类。有了这个&#34;总是类标签的想法&#34;您还可以有效地破坏从Java调用Scala代码的可能性。总之,从兼容性,性能或类大小的角度来看,要求类标记始终存在是没有任何意义的。