考虑下面的例子(OOP书籍中的典型例子):

我有一个动物班,每个动物都有很多朋友。 和子类,如狗,鸭,老鼠等,其中添加特定的行为,如bark(), quack()等。

下面是Animal类:

public class Animal {
    private Map<String,Animal> friends = new HashMap<>();

    public void addFriend(String name, Animal animal){
        friends.put(name,animal);
    }

    public Animal callFriend(String name){
        return friends.get(name);
    }
}

下面是一些带有大量类型转换的代码片段:

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

((Dog) jerry.callFriend("spike")).bark();
((Duck) jerry.callFriend("quacker")).quack();

有没有办法我可以使用返回类型的泛型来摆脱类型转换,这样我就可以说

jerry.callFriend("spike").bark();
jerry.callFriend("quacker").quack();

下面是一些初始代码,其中返回类型作为从未使用过的参数传递给方法。

public<T extends Animal> T callFriend(String name, T unusedTypeObj){
    return (T)friends.get(name);        
}

是否有一种方法可以在不使用instanceof的额外参数的情况下在运行时找出返回类型?或者至少通过传递该类型的类而不是虚拟实例。 我知道泛型是用于编译时类型检查的,但是对此有解决方法吗?


当前回答

还有另一种方法,当你重写一个方法时,你可以缩小返回类型。在每个子类中,您必须重写callFriend以返回该子类。代价是callFriend的多次声明,但是可以将公共部分隔离到内部调用的方法中。对我来说,这似乎比上面提到的解决方案简单得多,并且不需要额外的参数来确定返回类型。

其他回答

不。编译器无法知道jerry.callFriend("spike")将返回什么类型。此外,您的实现只是将强制转换隐藏在方法中,而没有任何额外的类型安全。考虑一下:

jerry.addFriend("quaker", new Duck());
jerry.callFriend("quaker", /* unused */ new Dog()); // dies with illegal cast

在这种情况下,创建一个抽象的talk()方法并在子类中适当地重写它会更好地为你服务:

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

jerry.callFriend("spike").talk();
jerry.callFriend("quacker").talk();

是什么

public class Animal {
    private Map<String,<T extends Animal>> friends = new HashMap<String,<T extends Animal>>();

    public <T extends Animal> void addFriend(String name, T animal){
        friends.put(name,animal);
    }

    public <T extends Animal> T callFriend(String name){
        return friends.get(name);
    }
}

我在我的lib kontraktor中做了以下事情:

public class Actor<SELF extends Actor> {
    public SELF self() { return (SELF)_self; }
}

子类化:

public class MyHttpAppSession extends Actor<MyHttpAppSession> {
   ...
}

至少这在当前类内部和具有强类型引用时是有效的。多重继承工作,但真的很棘手:)

不完全是,因为如你所说,编译器只知道callFriend()返回的是Animal,而不是Dog或Duck。

你不能给Animal添加一个抽象的makeNoise()方法吗?这个方法将被它的子类实现为树皮或嘎嘎声。

此外,您还可以通过这种方式要求该方法以给定类型返回值

<T> T methodName(Class<T> var);

更多示例请参见Oracle Java文档