有界类型参数:覆盖时无法访问子类型方法

Bounded type parameters: cannot access subtype methods when overriding

我刚开始在 Java 中使用有界类型,我不确定以下是因为继承使用不当导致的编程错误还是 javac 错误


我需要定义两种不同类型的对象:必须管理的事物和那些事物的管理者。这就是为什么我创建了一个抽象 class 来模拟那些东西的共同行为

public abstract class AbstractThing {
    // Common method implemented
    public void hello() {
        System.out.println("HI, I'm AbstractThing");
    }
} 

以及用于定义这些事物的管理器必须实现的方法的接口

public interface AbstractManager {
    // Operation that a things' manager must implement
    public <T extends AbstractThing> void greet(T t);
}

所以假设我创建了两个具体的东西 classes,其中一个只是继承了抽象的东西:

public class Thing extends AbstractThing {
    // Constructor
    public Thing() {}
}

但另一个实现了自己的方法:

public class AnotherThing extends AbstractThing {    
    // Constructor
    public AnotherThing() {}
    
    // Extra method which this class implements
    public void goodbye() {
        System.out.println("BYE, I'm AnotherThing");
    }
}

但是当我这样定义一个manager时:

public class Manager implements AbstractManager {
    
    // Constructor method
    public Manager() {}

    // Implementation of the interface's method fails
    @Override
    public <AnotherThing extends AbstractThing>
    void greet(AnotherThing t) {
        // I can use this method, which AnotherThing inherits from AbstractThing
        t.hello();
        // But I can't use this one defined by AnotherThing
        t.goodbye();
    }

}

我收到错误:

AnotherManager.java:15: error: cannot find symbol
        t.goodbye();
         ^
  symbol:   method goodbye()
  location: variable t of type AnotherThing
  where AnotherThing is a type-variable:
    AnotherThing extends AbstractThing declared in method <AnotherThing>greet(AnotherThing)
1 error

我不明白为什么,因为它将 class 识别为 AnotherThing,但将其处理为 AbstractThing。我试图将对象转换为子class,但它不起作用

我还检查过只有当我尝试访问子类型方法时才会发生这种情况,因为以下管理器编译并完美运行:

public class Manager implements AbstractManager {

    // Constructor method
    public Manager() {}
 
    // Implementation of the method defined into the interface
    @Override
    public <Thing extends AbstractThing>
    void greet(Thing t) {
        t.hello();
    }

    // I can overload the method "greet" without overriding the interface 
    // and it works for AnotherThing
    public void greet(AnotherThing t) {
        t.hello();
        t.goodbye();
    }

}

知道那里发生了什么吗?

原因如下

如果你这样做不难理解

public class Manager implements AbstractManager {
    
    // Constructor method
    public Manager() {}

    // Implementation of the interface's method fails
    @Override
    public <T extends AbstractThing>
    void greet(T t) {
        // I can use this method, which T inherits from AbstractThing
        t.hello();
        // But I can't use this one because T inherits from AbstractThing which does not know this method
        t.goodbye();
    }

}

换句话说,“extends”前面的词不应该是class名称,而是通用标识符(变量名)。

在这种情况下,由于 T(或无论您如何称呼它)是 AbstractThing,它不知道 goodbye() 是什么。

现在,为什么你的最后一个例子有效?

因为你说 greet 接收的是 AnotherThing 类型的对象,而不是像 T 这样的泛型类型。

AnotherThing中的<AnotherThing extends AbstractThing>与classAnotherThing无关。它是类型参数的名称,就好像它被称为 T 一样。它表现得像 AbstractThing 的原因是因为我们只知道它是这样的。您的方法不知道它是如何参数化的;它所知道的是它得到了 AbstractThing 的子 class。这对于我们需要捕获类型的构造很有用,但我们不关心它是什么子类型,例如:

<T extends AbstractThing> void copy(List<T> from, List<T> to) {
    for (T t : from) {
        t.hello();
        to.add(t);
    }
}

但是如果你想要AbstractManager的专门子class来管理AnotherThing,类型参数应该在class,而不是方法:

public interface AbstractManager<T extends AbstractThing> {
    // Operation that a things' manager must implement
    public void greet(T t);
}

public class AnotherManager implements AbstractManager<AnotherThing> {
    @Override
    public void greet(AnotherThing t) {
        t.hello();
        t.goodbye();
    }
}