向后兼容性将被完全破坏,真的。如果您有一个简单的方法,例如:
def foo[A](a: A)(implicit something: SomeType) = ???
那么假设在下一个版本的Scala中,编译器突然将隐式ClassTags添加到所有带类型参数的方法的签名中。这种方法会被打破。任何像foo(a)(someTypeValue) 这样被显式调用的地方都不再起作用。二进制和源代码兼容性将消失。
Java 互操作性会很糟糕。假设我们的方法现在看起来像这样:
def foo[A : ClassTag](a: A) = ???
因为ClassTags 是由 Scala 编译器生成的,所以在 Java 中使用这种方法会更加困难。您必须自己创建ClassTag。
ClassTag<MyClass> tag = scala.reflect.ClassTag$.MODULE$.apply(MyClass.class);
foo(a, tag);
我的 Java 可能不是 100% 正确,但你明白了。任何参数化的东西都会变得非常难看。好吧,如果它需要一个隐式的ClassTag,它已经是这样了,但是需要它的方法类会急剧增加。
此外,在我们(至少是我)使用的大多数参数化方法中,类型擦除并不是那么大的问题。由于上述原因,我认为自动为每个类型参数要求 ClassTag 会比它有帮助的麻烦得多。
当然这会增加更多的编译器开销,因为它需要生成比通常更多的ClassTags。我不认为它会增加更多的运行时开销,除非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。