简体   繁体   English

Java中的泛型子类型化问题

[英]Generics Subtyping issue in java

I am trying to learn Subtyping in Java and I am not an better person in generics so I am getting this issue or doubt- 我正在尝试学习Java的子类型化,而我在泛型方面并不是一个更好的人,所以我遇到了这个问题或疑问-

import java.util.ArrayList;
import java.util.Collection;

interface Animal<T>{}
class Lion implements Animal<Lion>{}
class Butterfly implements Animal<Butterfly>{}
class Cage<T>{
    public <T> void addAnimal(T t){

    }
}

interface CageAnimal<E> extends  Collection<E>{}
public class SubType<T> {

    public <T> SubType() {
        Lion lion = new Lion();
        Butterfly butterfly = new Butterfly();      
        /**
         * **Here inside Lion cage, we added Butterfly : WRONG**
         */
        Cage<Lion> cageLion = new Cage<Lion>();
        cageLion.addAnimal(lion);
        cageLion.addAnimal(butterfly);


        CageAnimal<Lion> cageAnimalLion = (CageAnimal<Lion>) new ArrayList<Lion>();
        cageAnimalLion.add(lion);

        //cageAnimalLion.add(butterfly);//Butterfly is Not Supposed to add here as it is the cage of Lion
    }   
}

In the above example when I declare Cage , why I am able to add Butterfly and in the Same case when I made CageAnimal type, I am not able to add any Buttefly 在上面的示例中,当我声明Cage时,为什么我可以添加Butterfly,而在我创建CageAnimal类型的相同情况下,我无法添加任何Buttefly

Cage<Lion> cageLion = new Cage<Lion>();
cageLion.addAnimal(lion);
cageLion.addAnimal(butterfly);

and in case of Cage 如果是笼子

Cage<Animal> cageAnimalLion = new Cage<Lion>();
cageAnimalLion.addAnimal(lion);
cageAnimalLion.addAnimal(butterfly); //Throwing Compile Error

Declare Cage class like this: 像这样声明Cage类:

class Cage<T extends Animal> {
    public void addAnimal(T t) { ... }
}

If you declare the addAnimal method in the following way... 如果您通过以下方式声明addAnimal方法...

public void <T> addAnimal(T t)

... you are "hiding" the T type parameter with a different type parameter with the same name. ...您正在“隐藏” T类型参数,且其名称具有不同的类型参数。 It is the same as if you declared the method like this: 就像您这样声明方法一样:

class Cage<T extends Animal> {
    public void <X> addAnimal(X t) { ... }
}

...which is obviously not doing its job. ...这显然没有做好。 On the other hand, in the first version I wrote, both the T in declaration of the class and the method are the same. 另一方面,在我编写的第一个版本中,类的声明中的T in和方法都相同。

Moreover declaring <T extends Animal> bound ensures that the cage can only be of type that extends an Animal , ie Cage<Lion> , Cage<Butterfly> , but Cage<String> is illegal. 此外,声明<T extends Animal>绑定可确保笼子只能是扩展Animal的类型,即Cage<Lion>Cage<Butterfly> ,但Cage<String>是非法的。

And of course, you cannot cast an ArrayList to CageAnimal , that will fail at runtime with a ClassCastException , because ArrayList in not a subtype of CageAnimal . 当然,您不能将ArrayList CageAnimalCageAnimal ,这将在运行时因ClassCastException而失败,因为ArrayList不是CageAnimal的子类型。

This line 这条线

 public <T> void addAnimal(T t){

should probably be 应该是

 public void addAnimal(T t){

Because CageAnimal and Cage are very different things. 因为CageAnimalCage是非常不同的东西。 Looks how you've defined generic parameter for Cage : 看看您如何为Cage定义通用参数:

public <T> void addAnimal(T t){

}

This <T> you put on the method, means that method has its own generic parameter, different from the one you've defined in class. 您在方法上放置的<T>表示该方法具有自己的通用参数,与您在类中定义的参数不同。 If you remove it from method signature it will use generic parameter of the class. 如果将其从方法签名中删除,它将使用该类的通用参数。

Eg 例如

public void addAnimal(T t)

Your problem is that fundamentally your Cage will accept any T , and therefore any Animal . 您的问题是,从根本上说,您的Cage将接受任何T ,因此也接受任何Animal The various T 's don't all refer to the same value of T, they're variables local to the class or method. 各种T并非都引用相同的T值,它们是类或方法局部的变量。

What you could write is something like this: 您可以编写如下内容:

public class Cage<T> {
  public void addAnimal(Animal<T> caged) {
  }
}

Now you will at least get compiler errors in the common case of: 现在,在以下常见情况下,至少会出现编译器错误:

Cage<Lion> c=new Cage<Lion>();
c.add(new Butterfly()); // should error AFAIK

However it will be reduced to a warning in case of: 但是,在以下情况下,它将减少为警告:

Animal butterfly=new Butterfly();
Cage<Lion> c=new Cage<Lion>();
c.add(butterfly); // warning about raw types... IIRC

Because, fundamentally Cage will still accept any Animal . 因为,从根本上说, Cage仍然可以接受任何Animal

EDIT: Note that the earlier mentioned answer of removing the <T> local to the addAnimal method will work better for this purpose. 编辑:请注意,前面提到的删除addAnimal方法中的<T>本地的addAnimal将对此更好地工作。

By declaring public <T> void addAnimal(T t) you're parameterising the method as well as the class Cage . 通过声明public <T> void addAnimal(T t) ,就可以对方法以及Cage类进行参数化。 This T has no relation to the T in Cage<T> . TCage<T>T没有关系。

You can either have: 您可以拥有:

class Cage<T extends Animal<T>> {
    public void addAnimal(T animal) {
    }
}

or, if you want the Animal returned then have: 或者,如果您希望Animal归还,则:

class Cage<T extends Animal<T>> {
    public T addAnimal(T animal) {
    }
}
class Cage<T>{
    public <T> void addAnimal(T t){

    }
}

The Cage class has a generic method addAnimal. Cage类具有通用方法addAnimal。 The generic type associated with the method causes the generic type associated with the class to be ignored and the type of the parameter to be used as the generic type for the method. 与该方法关联的通用类型会导致与该类关联的通用类型被忽略,而参数的类型将用作该方法的通用类型。

Try executing the following example to see what is happening: 尝试执行以下示例以查看发生了什么:

public class TestCage {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Cage<String> cage1 = new Cage<String>();
        cage1.addAnimal(new String("test1"));
        cage1.addAnimal(new Integer(1));
        cage1.addAnimal2(new String("test2"));
        //cage1.addAnimal2(new Integer(1));  //Uncomment to throw error
    }

}

class Cage<T>{
        public <T> void addAnimal(T t){
              System.out.println("T: " + t.getClass().getName());
        }

        public void addAnimal2(T t){
              System.out.println("T: " + t.getClass().getName());
        }
}

In summary, by adding a generic method to the class, the generic type parameter of the class is ignored and the type of the parameter passed into the method is used as the generic type parameter of the method. 总之,通过将通用方法添加到类中,将忽略该类的通用类型参数,并将传递给该方法的参数的类型用作该方法的通用类型参数。

尝试将<T>public <T> void addAnimal(T t)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM