Scala 扩展了 Java 泛型数组故障


  1. 我有一个带有通用参数的Java接口。
public interface JavaInterface<T> {
void sayHi(T[] keys);
}
  1. 我可以实现一个Java类,比如
public class JavaInterfaceImpl implements JavaInterface<String> {
@Override
public void sayHi(String[] array) {
}
}
  1. 但是,在创建 Scala 类来实现接口时,它失败了
class ScalaInterfaceImpl extends JavaInterface[String] {
override def sayHi(keys: Array[String]): Unit = {}
}

以下是错误消息。

class ScalaInterfaceImpl needs to be abstract, since method sayHi in trait JavaInterface of type (x$1: Array[String])Unit is not defined
(Note that Array[T with Object] does not match Array[String]: their type parameters differ)
class ScalaInterfaceImpl extends JavaInterface[String] {

method sayHi overrides nothing.
Note: the super classes of class ScalaInterfaceImpl contain the following, non final members named sayHi:
def sayHi(x$1: Array[String]): Unit
override def sayHi(keys: Array[String]): Unit = {}
  1. 但是,如果我为此trait实现 Scala 类,一切正常。
trait ScalaInterface[T] {
def sayHi(keys: Array[T]): Unit
}
  1. 1中的 Java 接口不可更改(来自第三方库),因此我在3中被阻止了。

更新 1.我发现它适用于以下代码

class ScalaInterfaceImpl extends JavaInterface[String] {
override def sayHi(keys: Array[String with Object]): Unit = {}
}

谁能帮忙解释一下?

泛型数组在两种语言之间表示略有不同。即使它们没有被类型擦除,它们的封闭泛型特征和接口也会在编译时T擦除它们的类型参数。

首先,考虑 Scala 示例:

trait ScalaInterface[T] {
def sayHi(keys: Array[T]): Unit
}

scalac编译后,它变成:

public interface ScalaInterface {
void sayHi(Object var1);
}

而 Java 接口:

public interface JavaInterface<T> {
void sayHi(T[] keys);
}

javac编译后,它变成:

public interface JavaInterface {
void sayHi(Object[] var1);
}

两者之间略有不同的签名乍一看无害,但实际上却有所不同。为什么 Scala 以不同的方式表示它们,这里解释了:

通用性呢?在 Java 中,您不能编写T[]T是 类型参数。那么Scala的Array[T]是如何表现的呢?事实上,一个 像Array[T]这样的泛型数组可以在运行时是Java的八个中的任何一个 基元数组类型byte[]short[]char[]int[]long[]float[]double[]boolean[],或者它可以是一个对象数组。唯一的 包含所有这些类型的公共运行时类型是AnyRef(或者, 等价于java.lang.Object),所以这就是 Scala 的类型 编译器映射Array[T].

当你使用JavaInterface<T>时,你实际上将类型Object作为类型T的上限,放在接口中使用T的每个位置,在本例中为sayHi方法。发生这种情况是因为类型擦除。

另一方面,泛型特征ScalaInterface[T]中的类型T不限于Object;它被限定为Any,因为这是Scala中的基本类型。这意味着T在方法sayHi中两种语言之间具有不同的上限。

因此,即使JavaInterface<T>ScalaInterface[T]看起来相似,它们的sayHi方法也不是真正等效的。scalac编译器在后台执行一些类型推断,检查使用类型T的位置,并得出结论,sayHi方法需要以下类型约束:

trait ScalaInterface[T] {
def sayHi(keys: Array[T with Object]): Unit
}

这实际上是如何处理您的JavaInterface以及为什么使用String with Object可以解决问题。

出现的一个问题是,为什么 Scala 不使用Object作为泛型类型的上限:

trait ScalaInterface[T <: Object] {
def sayHi(keys: Array[T]): Unit
}

虽然这适用于这个特定的用例,但它的限制太大了,因为你永远无法在类型T中使用AnyVal的子类型,所以ScalaInterface[Int]ScalaInterface[Double]是不可能的,只是因为接口有一个将泛型数组作为参数的方法。

如果您的接口没有将泛型数组作为参数的方法,则根本不需要此约束。或者,如果您的接口有另一个方法,该方法采用类型为T的简单参数,那也会受到限制。这些考虑表明问题实际上不在于类型参数T,而在于方法sayHi,所以这是问题被精确定位和包含的地方。

最新更新