考虑下面的例子(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的额外参数的情况下在运行时找出返回类型?或者至少通过传递该类型的类而不是虚拟实例。 我知道泛型是用于编译时类型检查的,但是对此有解决方法吗?


当前回答

正如你所说,通过一门课程就可以了,你可以这样写:

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

然后像这样使用它:

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

这并不完美,但这几乎是您使用Java泛型所能达到的极限。有一种方法可以使用超级类型令牌来实现类型安全异构容器(THC),但这也有它自己的问题。

其他回答

不可能的。如果只给一个String键,Map怎么知道它将得到Animal的哪个子类呢?

唯一可能的方法是每个Animal只接受一种类型的friend(那么它可以是Animal类的一个参数),或者callFriend()方法获得了一个类型参数。但是,看起来您确实忽略了继承的要点:在独占地使用超类方法时,只能统一地对待子类。

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

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

更多示例请参见Oracle Java文档

正如你所说,通过一门课程就可以了,你可以这样写:

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

然后像这样使用它:

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

这并不完美,但这几乎是您使用Java泛型所能达到的极限。有一种方法可以使用超级类型令牌来实现类型安全异构容器(THC),但这也有它自己的问题。

你可以这样实现它:

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

(是的,这是法律代码;参见Java泛型:仅定义为返回类型的泛型类型。)

返回类型将从调用者推断出来。但是,请注意@SuppressWarnings注释:它告诉您这段代码不是类型安全的。您必须自己验证它,或者您可以在运行时获得classcastexception。

不幸的是,你使用它的方式(没有将返回值分配给临时变量),唯一让编译器高兴的方法是像这样调用它:

jerry.<Dog>callFriend("spike").bark();

虽然这可能比强制转换要好一点,但正如David Schmitt所说,最好给Animal类一个抽象的talk()方法。

这里有很多很好的答案,但这是我在Appium测试中采用的方法,在该测试中,对单个元素的操作可能导致基于用户设置的不同应用程序状态。虽然它没有遵循OP的例子的惯例,但我希望它能帮助到一些人。

public <T extends MobilePage> T tapSignInButton(Class<T> type) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //signInButton.click();
    return type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
}

MobilePage是该类型扩展的超类,这意味着您可以使用它的任何子类(胡说) type.getConstructor(Param.class等)允许您与 类型的构造函数。这个构造函数在所有期望的类之间应该是相同的。 newInstance接受一个声明的变量,您希望将该变量传递给new objects构造函数

如果你不想抛出错误,你可以像这样捕获它们:

public <T extends MobilePage> T tapSignInButton(Class<T> type) {
    // signInButton.click();
    T returnValue = null;
    try {
       returnValue = type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return returnValue;
}