4

今天我遇到了一个让我非常想知道的函数。因此,让我们假设这个简单的结构以进行澄清。

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
  }
}

现在将 a 分配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 如何让我在猫/狗上分配动物,以及它必须投射的动物的专业化

4

3 回答 3

5

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

  • 诸如此类的特征<A extends Animal> A getAnimalByName(String name)涉及一种称为类型推断的技术——即从赋值的左侧推断出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,并且编译器会发出警告。这是泛型的基本保证 - 如果您的代码在没有警告的情况下编译,它不会破坏类型安全。

于 2012-07-12T17:59:15.273 回答
0

我不能 100% 确定这一点,但我怀疑它与多态性有关,并且编译器将首先寻找“本地”(在对象的直接类环境中)方法进行实现,然后再向上执行找到实现。

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

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

于 2012-07-12T17:54:45.517 回答
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;
} 
于 2012-07-12T18:09:45.193 回答