Why doesn't Scala "handle" ClassTags automatically?
Class 标签似乎真的很容易使用,但是,至少在某些情况下(如果不是全部)总是可靠地自动插入 ClassTag 样板在数学上是不可能的吗?考虑这个例子:
1 2 3 4 5
| def foo [T : ClassTag ] = {
val cls = classTag [T ]. runtimeClass. asInstanceOf[Class [T ]]
val myList : Seq [T ] = parseList (rawJson, cls ) // let's assume parseList a library method that needs to work with a Class instance (e.g. RestFB's fetchConnnection)
...
} |
为什么 Scala 不让我改写:
1 2 3 4 5
| def foo [T ] = {
val cls = classOf [T ]
val myList : Seq [T ] = parseList (rawJson, cls ) // let's assume parseList a library method that needs to work with a Class instance (e.g. RestFB's fetchConnnection)
...
} |
...并自动将后者转换为前者?
这是否有技术原因,例如类标签的可取性不能总是由编译器/反射库可靠地确定?或者它纯粹是一个有意识的设计选择来保持这个明确?例如,鼓励程序员首先避免使用类标签以获得更简洁的设计和更好的性能,即使知道在许多情况下这是不可能的?
附言我不是在问为什么 Scala 不(也不建议它应该)将 ClassTag 魔法添加到具有泛型类型参数的每个方法定义中;我只是在问为什么它不能在需要/不可避免时自动完成。
这个语法:
1
| def foo [T : ClassTag ] = ... |
其实是这个的简写:
这意味着如果为每个泛型类型参数提供了 ClassTag[T],那么每个函数都会有附加的隐藏参数,甚至有几个。
这当然是完全不可取的。这种方法最突出的缺点之一是在使用泛型时几乎破坏了 Java 互操作性:
1 2
| SomeClass someClass = new SomeClass ();
someClass. foo<Integer >(ClassTag. apply(Integer. class)); |
想象一下更高级的泛型方法和参数。这只是为了能够匹配泛型类型,这几乎不需要。
调用 Java 泛型方法也是模棱两可的。这里应该调用哪个方法:
1 2
| val jc = new JavaClass
jc. foo[Int ]() |
如果 JavaClass 是这样定义的:
1 2 3 4
| public class JavaClass {
public < T > void foo () { ... }
public < T > void foo (ClassTag < T > ct ) { ... }
} |
?
当然,如果 Java 支持物化泛型,一切都会不同...
- "为每个泛型类型参数提供"——但我没有说转换应该每次都发生......只有当它\\是不可避免的时候。
-
@ErikAllik,这意味着非常不稳定的 API。假设您更改了以前不需要 ClassTag 的代码,因此它开始依赖它。然后,您的 API 的所有用户都可能会中断,而您甚至都不会注意到。当您的泛型方法覆盖另一个方法时,您甚至可以破坏自己的代码 - 突然它将停止覆盖其父方法。此外,这意味着您无法完全控制方法的签名。我当然不喜欢那样。
-
好的,这是一个好点;我会再等一会儿,看看其他人是否能提出一些额外的理由。