java接口中的泛型方法如何将超级对象转换为具体的子类对象?

java接口中的泛型方法如何将超级对象转换为具体的子类对象?,java,generics,interface,polymorphism,Java,Generics,Interface,Polymorphism,我有以下代码片段,它运行良好 public class Test { public static void main(String[] args) { App app = new App(); app.setHandler(Apple.class, new AppleHandler()); app.setHandler(Banana.class, new BananaHandler()); app.process(new

我有以下代码片段,它运行良好

public class Test {
    public static void main(String[] args) {
        App app = new App();
        app.setHandler(Apple.class, new AppleHandler());
        app.setHandler(Banana.class, new BananaHandler());

        app.process(new Apple());
        app.process(new Banana());
    }
}

class Fruit {}
class Apple extends Fruit {}
class Banana extends Fruit {}

interface Handler<T extends Fruit> {
    public void handle(T fruit);
}

class AppleHandler implements Handler<Apple> {
    @Override
    public void handle(final Apple fruit) {
        System.out.println("This is an apple.");
    }
}

class BananaHandler implements Handler<Banana> {
    @Override
    public void handle(final Banana fruit) {
        System.out.println("This is a banana.");
    }
}

class App {
    Map<Class, Handler> handlerMap = new HashMap<>();

    public void setHandler(Class clazz, Handler handler) {
        handlerMap.put(clazz, handler);
    }

    public void process(Fruit fruit) {
        Handler handler = handlerMap.get(fruit.getClass());
        handler.handle(fruit);// HERE, how java convert Fruit object to concrete subclass object automatically?
    }
}
公共类测试{
公共静态void main(字符串[]args){
App App=新App();
app.setHandler(Apple.class,新的AppleHandler());
app.setHandler(Banana.class,新的BananaHandler());
app.process(新苹果());
app.process(新香蕉());
}
}
水果类{}
苹果类扩展水果{}
类{}
接口处理程序{
公共空柄(T果);
}
类AppleHandler实现处理程序{
@凌驾
公共无效手柄(最终苹果果){
System.out.println(“这是一个苹果”);
}
}
类BananaHandler实现Handler{
@凌驾
公共空柄(最终香蕉果){
System.out.println(“这是一个香蕉”);
}
}
类应用程序{
Map handlerMap=新的HashMap();
公共void setHandler(类clazz,Handler){
handlerMap.put(clazz,handler);
}
公共无效流程(水果){
Handler=handlerMap.get(fruit.getClass());
handle.handle(fruit);//这里,java如何自动将fruit对象转换为具体的子类对象?
}
}

我希望
App
类以不同的方式处理不同的
Fruit
,因此我定义
AppleHandler
来处理
Apple
,定义
BananaHandler
来处理
BananaHandler
AppleHandler
BananaHandler
都实现了一个通用接口
Handler
,该接口具有通用方法
handle
handle
方法在
App.process
方法中被赋予一个
Fruit
对象,具体的
handle
方法将按预期被调用。当调用具体的
handle
方法时,java似乎会自动将
Fruit
对象转换为其实际类型。java是如何做到这一点的?

代码之所以有效,是因为您在
映射中使用
处理程序
来利用原始类型

通过使用原始类型,您可以获得类似于
Map
,因此任何类型的对象都可以用于检索正确的
处理程序。代码工作的事实仅仅是由映射器设置正确这一事实给出的。没有什么能阻止你这样做

app.setHandler(Apple.class, new BananaHandler());
app.setHandler(Banana.class, new AppleHandler());
这将产生一个
ClassCastException

tests.Main$Apple cannot be cast to tests.Main$Banana
所以这里没有黑魔法,只要尝试使用泛型类型变量,您就会看到,如果不强制执行
处理程序的类型,相同的代码将无法工作

由于类型擦除,您的
AppleHandler
实现编译为

class AppleHandler {
  void handle(Object object) {
    Apple apple = (Apple)object;
    System.out.println("This is an apple");
  }
}
因此,从外部不需要强制转换就可以将结果分派到正确的处理程序,但这也意味着编译器无法确保在运行时分派是正确的

public void process(Fruit fruit) {
    Handler handler = handlerMap.get(fruit.getClass());

在这里,参数是一个
水果
,但实际的类是
香蕉
。所以当你说fruit.getClass()返回香蕉时,它得到香蕉处理程序,我认为如果你去掉处理程序接口,把句柄()方法放在水果/苹果/…
中,这个例子会更好

在java中,我们通过引用变量访问对象(例如,type
T
),这是唯一的方法。一旦为对象(T或T的子类型)创建了ref变量,则无法更改引用变量的类型,可以将其重新分配给其他变量。引用变量的类型将决定它可以在对象上调用的方法

您使用
new Apple()
创建一个对象,一个ref-var被声明给Apple对象,无论它是
four a=new Apple()
还是
Apple a=new Apple()
两者都引用
Apple
的对象,即使以后您也会引用
object o=a
o
也指
Apple
对象。所以如果你做了
Fruit a=new Apple();a、 doSomething()
,java将找到
Apple
对象,并执行
Apple
类/对象的
doSomething()
方法

似乎java在调用具体句柄方法时会自动将一个水果对象转换为它的真实类型。java是如何实现这一点的

这是正确的。编译器根据泛型参数插入强制转换。这在代码中很难看到,因为您使用了太多的原始类型

当您这样做时:

Handler handler = handlerMap.get(fruit.getClass());
handler.handle(fruit);
handler
的泛型参数未知,因此将
fruit
传递给
handle
方法的已擦除形式,如下所示:

public void handle(Fruit fruit)
因此代码可以编译。但是在这一点上,动态分派启动了一个调用
句柄
的重写实现,例如您在
AppleHandler
中定义的实现:

@Override
public void handle(final Apple fruit) {
    System.out.println("This is an apple.");
}
此时,通用参数已知,参数被转换为
Apple

此强制转换也可以在
AppleHandler
类的字节码中看到:

 public void handle(test.Fruit);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #35                 // class test/Apple
       5: invokevirtual #37                 // Method handle:(Ltest/Apple;)V
       8: return

你的问题是:Java是如何实现多态性的?@DavidChoweller,不是。我的问题是:当调用具体句柄方法(这称为多态性)时,Java是如何将水果对象转换为苹果或香蕉对象的?是的,你是对的。但是,Banana.handle被赋予了一个水果物体,它是如何以及何时铸造成香蕉物体的?@expoter no,你很困惑。这个过程得到了一个香蕉。香蕉是水果的一个子类,因此当调用process方法时,水果对象实际上是一个香蕉,而不是一个正在进行多态性的水果。没有转换发生。谢谢你的建议!