Java如何解决这种无类型泛型方法?

时间:2012-07-12 17:39:47

标签: java generics casting weak-linking

今天我遇到一个让我真的很奇怪的功能。因此,我们假设这个简单的结构可以进行清理。

public class Animal{

  public String getName(){ return null; }

}

public class Dog extends Animal{

  @Override
  public String getName(){
    //I'm aware that not any Dog's name is 'Pluto', but its just a Sample ;)
    return "Pluto"
  }

}

public class Cat extends Animal{

  protected final String mName;  

  public Cat(String name){
    mName = name;
  }

  @Override
  public String getName(){
     //cats have different names, because the internet loves cats
    return mName;
  }

  public void miao(){
    //just a dummy
  }
}

现在将Dog分配给Animal指针是绝对有效的,但将Animal分配给Dog指针无效,如下所示:

Animal animal = new Dog(); //valid, any Dog is at least an Animal
Dog dog = new Animal();  // invalid, of course not any Animal is a Dog!

让我们假设一个AnimalCage类,其中发生了“魔术”:

public class AnimalCage{

  private ArrayList<Animal> mCage = new ArrayList<Animal>();

  public addAnimal(Animal animal){
    mCage.add(animal);
  }

  // HERE is where the "Magic" happens:
  public <A extends Animal> A getAnimalByName(String name){
    //try catch block not mandatory
    try{
      for (Animal a: mCage){
        if (name.equals(a.getName()) return (A)a; 
      }
    } catch(ClassCastException cce){}
    return null;
  }
}

使用AnimalCage可以执行此操作:

//all valid
AnimalCage cage = new AnimalCage();
Dog dog = new Dog();
Cat cat = new Cat("Mauzi");
Cat cat2 = new Cat("Garfield");
cage.add(dog);
cage.add(cat);
cage.add(cat2);
// and later get it back
//will return dog
Dog pluto = cage.getAnimalByName("Pluto"); 
//will find nothing and return null
Dog snoopy = cage.getAnimalByName("Snoopy);
//will raise ClassCastException and return null 
snoopy = cage.getAnimalByName("Mauzi"); 
//will return Mauzi
Animal mauzi = cage.getAnimalByName("Mauzi");

所以我可以做任何事情而不用明确表达。这导致我假设Erasures在运行时没有被删除,尽管我知道的更好。之前我认为我必须至少给出一个关于如何投射这个函数的指标:

public <A extends Animal> A getAnimalByName(String name, Class<A> animalClass){
  try{
    for (Animal a: mCage){
      if (name.equals(a.getName()) return (A)a;          }
  } catch(ClassCastException cce){}
  return null;
}

//use
Dog dog = cage.getAnimalByName("Pluto", Dog.class);

我真的很想知道Java如何让我在Cats / Dogs上分配动物,以及它有什么专业化的动物来施展

3 个答案:

答案 0 :(得分:5)

我不太明白你的问题,但也许我可以澄清一些观点:

  • <A extends Animal> A getAnimalByName(String name)之类的Singnature涉及一种名为类型推断的技术 - 即A的实际类型,特别是getAnimalByName()的调用是从任务的左侧。

    请注意,这是一个纯粹的编译时功能 - 代码如

    <A extends Animal> A getAnimalByName(String name) { ... }
    ...
    Dog dog = getAnimalByName("foo");
    
    编译时

    变成以下代码(由于类型擦除):

    Animal getAnimalByName(String name) { ... }
    ...
    Dog dog = (Dog) getAnimalByName("foo");
    
  • 正如您所看到的,您的代码打破了类型安全保证 - 当您在return (A) a中执行转换时会发生这种情况,并且编译器会发出有关它的警告。它是泛型的基本保证 - 如果您的代码在没有警告的情况下编译,则不会破坏类型安全。

答案 1 :(得分:0)

我无法100%确定这一点,但我怀疑它与Polymorphism有关并且编译器将首先寻找“local”(在对象的直接类环境中)方法在上传链之前执行实现。

即使您可以将Cat视为动物,但在使用Animal实现之前,底层代码仍会尝试首先找到Cat实现,因为当您实例化对象时,编译器会知道该类。

因为getName()是为Animal定义的,所以假设它至少可以找到THAT实现(即使Cat改变了它)。狗也是如此。

答案 2 :(得分:0)

您将永远不会在以下代码中遇到ClassCastException,它显示泛型类型被删除,

public <A extends Animal> A getAnimalByName(String name, Class<A> animalClass){
    try {
        for (Animal a: mCage){
            if (name.equals(a.getName()) return (A)a;          
        }
    } catch(ClassCastException cce){}
    return null;
} 

如果你想写它做的事情:

public <A extends Animal> A getAnimalByName(String name, Class<A> animalClass){
    try {
        for (Animal a: mCage){
            if (name.equals(a.getName()) 
                return animalClass.cast(a);          
        }
    } catch(ClassCastException cce){}
    return null;
}