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 を Pointer に割り当てることは絶対に有効ですが、次のDogように an をPointerAnimalに割り当てることは無効です。AnimalDog

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

私はあなたの質問を完全には理解していませんが、おそらく私はいくつかの点を明確にすることができます:

  • このようなSingnatureには、型推論<A extends Animal> A getAnimalByName(String name)と呼ばれる手法が含まれます。つまり、特にの呼び出しの実際の型は、割り当ての左側から推論されます。AgetAnimalByName()

    これは純粋にコンパイル時の機能であることに注意してください-次のようなコード

    <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

次のコードで 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 に答える
0

これについて 100% 確信を持てるわけではありませんが、ポリモーフィズムと、コンパイラがチェーンを上る前に実装のために最初に (オブジェクトの直接のクラス環境で) 「ローカル」メソッドを探すという事実に関係していると思われます。実装を見つけます。

Cat を Animal として扱う場合でも、オブジェクトをインスタンス化したときにコンパイラがクラスを認識しているため、基になるコードは Animal 実装を使用する前に Cat 実装を最初に見つけようとします。

getName() は Animal に対して定義されているため、少なくともその実装を見つけることができると想定されています (たとえ Cat が変更したとしても)。同じことが犬にも当てはまります。

于 2012-07-12T17:54:45.517 に答える