- 我有一个带有通用参数的Java接口。
public interface JavaInterface<T> {
void sayHi(T[] keys);
}
- 我可以实现一个Java类,比如
public class JavaInterfaceImpl implements JavaInterface<String> {
@Override
public void sayHi(String[] array) {
}
}
- 但是,在创建 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 = {}
- 但是,如果我为此
trait
实现 Scala 类,一切正常。
trait ScalaInterface[T] {
def sayHi(keys: Array[T]): Unit
}
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
,所以这是问题被精确定位和包含的地方。