我想编写如下重载函数:
case class A[T](t: T)
def f[T](t: T) = println("normal type")
def f[T](a: A[T]) = println("A type")
结果正如我所料:
f(5) => normal type
f(A(5)) => A type
到现在为止还挺好。但问题是同样的事情不适用于数组:
def f[T](t: T) = println("normal type")
def f[T](a: Array[T]) = println("Array type")
现在编译器提示:
double definition: method f:[T](t: Array[T])Unit and method f:[T](t: T)Unit at line 14 have same type after erasure: (t: java.lang.Object)Unit
我认为类型删除后的第二个函数的签名应该是 (a: Array[Object])Unit 而不是 (t: Object)Unit,所以它们不应该相互冲突。我在这里缺少什么?
如果我做错了什么,写 f 的正确方法是什么,以便根据参数的类型调用正确的方法?
最佳答案
这在 Java 中从来都不是问题,因为它不支持泛型中的原始类型。因此,以下代码在 Java 中是非常合法的:
public static <T> void f(T t){out.println("normal type");}
public static <T> void f(T[] a){out.println("Array type");}
另一方面,Scala 支持所有类型的泛型。尽管 Scala 语言没有原语,但生成的字节码将它们用于 Int、Float、Char 和 Boolean 等类型。它使 Java 代码和 Scala 代码有所不同。 Java 代码不接受
int[]
作为数组,因为 int
不是 java.lang.Object
.所以Java可以将这些方法参数类型删除到Object
和 Object[]
. (这意味着 Ljava/lang/Object;
和 [Ljava/lang/Object;
在 JVM 上。)另一方面,您的 Scala 代码处理所有数组,包括
Array[Int]
, Array[Float]
, Array[Char]
, Array[Boolean]
等等。这些数组是(或可以是)原始类型的数组。它们不能被强制转换为 Array[Object]
或 Array[anything else]
在 JVM 级别。 Array[Int]
只有一个父类(super class)型和 Array[Char]
: 是java.lang.Object
.这是您可能希望拥有的更一般的父类(super class)型。为了支持这些陈述,我编写了一个使用较少通用方法 f 的代码:
def f[T](t: T) = println("normal type")
def f[T <: AnyRef](a: Array[T]) = println("Array type")
此变体的工作方式类似于 Java 代码。这意味着,不支持原语数组。但是这个小小的改变足以让它编译。另一方面,由于类型删除原因,无法编译以下代码:
def f[T](t: T) = println("normal type")
def f[T <: AnyVal](a: Array[T]) = println("Array type")
添加@specialized 并不能解决问题,因为生成了一个泛型方法:
def f[T](t: T) = println("normal type")
def f[@specialized T <: AnyVal](a: Array[T]) = println("Array type")
我希望@specialized 可能已经解决了这个问题(在某些情况下),但编译器目前不支持它。但我认为这不会是 scalac 的高优先级增强。
关于Scala:数组和类型删除,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14355092/