通用:为什么使用“class A <e extends =”“superclass =”“>”而不是“class B <superclass>”?

时间:2016-03-13 17:23:26

标签: java generics

我正在尝试使用Java泛型。我理解Java泛型,我们可以创建只处理特定类型的类和方法。这样可以在编译时检测编程错误。

我的问题可能听起来很奇怪。为什么要使用E extends Superclass代替Superclass?请看下面的代码。

class Hunter <E extends Animal>{}

class Keaper <Animal>{}
    /*edit: as AR.3 and other pointed out, Keaper<Animal> is the same as Keaper<E>.
 Animal  is just a type parameter here*/

class Animal{}

class Cat extends Animal{}

class Dog extends Animal{}


public class TestGeneric {
    public static void main(String[] args) {
        Hunter<Cat> hunter1 = new Hunter<>();
        Hunter<Dog> hunter2 = new Hunter<>();
        Hunter<Animal> hunter3 = new Hunter<>();

        ArrayList<Hunter> hunters=  new ArrayList<>();
        hunters.add(hunter1);
        hunters.add(hunter2);
        hunters.add(hunter3);

        Keaper<Cat> keaper1 = new Keaper<>();
        Keaper<Dog> keaper2 = new Keaper<>();
        Keaper<Animal> keaper3 = new Keaper<>();
//Edit: as AR.3 and others pointed out, Keaper<String> is also legal here.

        ArrayList<Keaper> keapers=  new ArrayList<>();
        keapers.add(keaper1);
        keapers.add(keaper2);
        keapers.add(keaper3);
    }
}

我认为E extends Animal几乎与Animal相同,但第一个可能会提供更多信息。有什么想法吗?

2 个答案:

答案 0 :(得分:15)

实际上在Animal类的定义中声明的Keaper只不过是类型参数(对于它来说,使用一个字母就更常规了,比如Keaper<T>)。因此,它是一个完全不同的可能类型参数范围:Keaper类可以接受任何类型作为参数,而不仅仅是Animal s。您可以编写Keaper<String>并且编译正常,这显然不是您想要的。

使用泛型无法实现您的目的:您不能强制某个类是通用的,只有一个可能的类型参数与之相关联。你可以做的是限制相关类型的范围,例如,扩展某种类型的任何类型,你已经使用Hunter类做了。

同样如@JHH评论中所述,您应该避免在列表中使用Keaper作为原始类型,否则代码将不是完全通用的。您可以写ArrayList<Keaper> keapers = new ArrayList<>()而不是ArrayList<Keaper<? extends Animal>> keapers = new ArrayList<>()

答案 1 :(得分:10)

通常,当您想要保留in-parameter的特定类型时,您将使用extends。考虑这个例子:

public class TestGeneric {

    public static <T extends Animal> T changeExtendedAnimal(T extendedAnimal) {
        return extendedAnimal;
    }

    public static Animal changeAnimal(Animal animal) {
        return animal;
    }

    public static void main(final String[] args) {
        Cat changedCat1 = changeExtendedAnimal(new Cat()); //Compiles fine. 
        Cat changedCat2 = changeAnimal(new Cat()); //Won't compile: Type mismatch: cannot convert from Animal to Cat
    }
}

此处changeExtendedAnimal()方法使用extends,因此如果您使用Cat调用方法,编译器会知道您将返回Cat
另一方面,使用changeAnimal()方法,编译器无法知道这一点,并且您会收到编译时错误。