Java泛型、类型推断、继承?
我正在阅读泛型的类型推断,这段代码是作为一个未能编译的示例提供的
import java.io.*;
class LastError<T> {
private T lastError;
void setError(T t){
lastError = t;
System.out.println("LastError: setError");
}
}
class StrLastError<S extends CharSequence> extends LastError<String>{
public StrLastError(S s) {
}
void setError(S s){
System.out.println("StrLastError: setError");
}
}
class Test {
public static void main(String []args) {
StrLastError<String> err = new StrLastError<String>("Error");
err.setError("Last error");
}
}
书中给出的解释是:
"(It looks like the
setError()
method inStrLastError
is overridingsetError()
in theLastError
class. However, it is not the case. At the time of compilation, the knowledge of typeS
is not available. Therefore, the compiler records the signatures of these two methods assetError(String)
in superclass andsetError(S_extends_CharSequence)
in subclass—treating them as overloaded methods (not overridden). In this case, when the call tosetError()
is found, the compiler finds both the overloaded methods matching, resulting in the ambiguous method call error."
我真的不明白为什么不能在编译时推断类型S
。
^调用类StrLastError
的构造函数时传递{String
确实实现了接口CharSequence
,
那么这不意味着S
for <S extends CharSequence>
实际上是String
类型吗
关于泛型的Java在线教程我已经读了好几遍。我检查了“类型推断”和继承,我只是不知道整个过程是如何工作的。我真的需要解释一下这个问题
我的重点是:
- 如果子类型不能决定
S
,为什么超类型可以决定T
,因为超类没有上限?或者它是否推断T
是String
,因为子类型首先调用超类型的构造函数李> 我理解,如果构造函数被调用为:
StrLastError<CharSequence> err = newStrLastError<>((CharSequence)"Error");
不会有歧义,因为它是简单的方法重写。(或者我在这里是不是错了?)
然而,正如我在开头所说的,如果String
被通过,为什么S
不能被推断为String
# 1 楼答案
超级类型不是决定或推断
T
是什么;通过这个声明,你明确地告诉了它T
T
现在被绑定到String
forLastError
,这使得父类a中的每个对T
的引用都是具体的String
您的子类现在有一个绑定
S extends CharSequence
,但它独立于应用于父类的绑定现在的情况是,Java将编译您的子类,子类的结果是创建两个签名与
String
匹配的方法。(这里的关键提示:AString
是-ACharSequence
。)在子类中,
setError(Ljava/lang/CharSequence;)V
被生成为setError
的签名。由于泛型的工作方式,LastError#setError
将被视为具有setError(Ljava/lang/String;)V
的签名。这也是为什么在实际重写该方法时,它会将String
类型作为参数,而不是其他任何类型所以,我们得到的是two methods that have override-equivalent signatures.
JLS 8.4.8.4。适用于这里
不,现在你在搞raw types。有趣的是,这将起作用,主要是因为这两种方法的特征已经变成:
您希望使用泛型来避免这样的情况;您可能希望在某个时候调用super class方法,但在这种使用这些绑定的场景中,很难实现
# 2 楼答案
你必须提醒自己,这些类是一个接一个地编译的。Java泛型不像其他语言那样是模板。只会有一个编译类,而不是与它一起使用的每个类型都有一个类
通过这种方式,您可以看到类
StrLastError
需要以这样一种方式编译,即它也可以与实现CharSequence
作为泛型类型S的其他类一起使用这就是为什么编译器会给你两个不同的方法,而不是一个重写的方法。现在,要知道子类可能只在类型建议的情况下,就想重写父类中的方法,这将是一个运行时任务。由于开发人员很难理解这种行为,并且可能会导致编程错误,因此它引发了一个例外
如果使用CharSequence作为类
StrLastError
的泛型类型参数,则将调用父类中的setError
方法,因为"Last Error"
的类型是String
,这比CharSequence
更具体,Java总是选择最具体的方法以防重载。(我希望很明显,在这种情况下,该方法也没有被覆盖)# 3 楼答案
这是一个棘手的例子,要解决它,您需要更改以下行: