有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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 in StrLastError is overriding setError() in the LastError class. However, it is not the case. At the time of compilation, the knowledge of type S is not available. Therefore, the compiler records the signatures of these two methods as setError(String) in superclass and setError(S_extends_CharSequence) in subclass—treating them as overloaded methods (not overridden). In this case, when the call to setError() is found, the compiler finds both the overloaded methods matching, resulting in the ambiguous method call error."

我真的不明白为什么不能在编译时推断类型S。 ^调用类StrLastError的构造函数时传递{}, 从API文档来看,String确实实现了接口CharSequence, 那么这不意味着Sfor <S extends CharSequence>实际上是String类型吗

关于泛型的Java在线教程我已经读了好几遍。我检查了“类型推断”和继承,我只是不知道整个过程是如何工作的。我真的需要解释一下这个问题

我的重点是:

  1. 如果子类型不能决定S,为什么超类型可以决定T,因为超类没有上限?或者它是否推断TString,因为子类型首先调用超类型的构造函数
  2. 我理解,如果构造函数被调用为:

    StrLastError<CharSequence> err = newStrLastError<>((CharSequence)"Error");
    

    不会有歧义,因为它是简单的方法重写。(或者我在这里是不是错了?)

然而,正如我在开头所说的,如果String被通过,为什么S不能被推断为String


共 (3) 个答案

  1. # 1 楼答案

    If the subtype can't decide S, how come the super type can decide T, because the superclass does not have an upper bound? Or does it infer that T is String because the subtype calls the supertype's constructor first?

    超级类型不是决定或推断T是什么;通过这个声明,你明确地告诉了它T

    class StrLastError<S extends CharSequence> extends LastError<String>
    

    T现在被绑定到Stringfor LastError,这使得父类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.

    void setError(CharSequence s)
    void setError(String s)
    

    JLS 8.4.8.4。适用于这里

    It is possible for a class to inherit multiple methods with override-equivalent signatures (§8.4.2).

    It is a compile-time error if a class C inherits a concrete method whose signature is a subsignature of another concrete method inherited by C. This can happen if a superclass is generic, and it has two methods that were distinct in the generic declaration, but have the same signature in the particular invocation used.


    I understand that if the Constructor is invoked as StrLastError err = new StrLastError<>((CharSequence)"Error"); there will be no ambiguity, since its plain method overriding then.(Or I'm even wrong here)

    不,现在你在搞raw types。有趣的是,这将起作用,主要是因为这两种方法的特征已经变成:

    void setError(Object s)
    void setError(String s)
    

    您希望使用泛型来避免这样的情况;您可能希望在某个时候调用super class方法,但在这种使用这些绑定的场景中,很难实现

  2. # 2 楼答案

    你必须提醒自己,这些类是一个接一个地编译的。Java泛型不像其他语言那样是模板。只会有一个编译类,而不是与它一起使用的每个类型都有一个类

    通过这种方式,您可以看到类StrLastError需要以这样一种方式编译,即它也可以与实现CharSequence作为泛型类型S的其他类一起使用

    这就是为什么编译器会给你两个不同的方法,而不是一个重写的方法。现在,要知道子类可能只在类型建议的情况下,就想重写父类中的方法,这将是一个运行时任务。由于开发人员很难理解这种行为,并且可能会导致编程错误,因此它引发了一个例外

    如果使用CharSequence作为类StrLastError的泛型类型参数,则将调用父类中的setError方法,因为"Last Error"的类型是String,这比CharSequence更具体,Java总是选择最具体的方法以防重载。(我希望很明显,在这种情况下,该方法也没有被覆盖)

  3. # 3 楼答案

    这是一个棘手的例子,要解决它,您需要更改以下行:

    class StrLastError<S extends CharSequence> extends LastError<S>