Причина, по которой это сбивает с толку, заключается в том, что это не то, что вы обычно делаете в реальном коде, за исключением случая с Фабрикой.
Как указывалось в комментарии Забузы, Вы можете сделать это, потому что Cat
'is-a' является видом Animal
, и поэтому вы можете назначить объект типа Cat
объекту типа Animal
. Но вы, конечно, не можете выполнить задание иначе, потому что Animal
- это не Cat
.
Теперь есть некоторые скрытые проблемы, которые связаны с возможностью создания экземпляра супертипа, а также подтипа, что означает, что вы обычно не будете делать это в реальном коде, потому что это сильно усложняет. вещей в будущем. Скорее всего, вы бы сделали Animal
интерфейсом и классом GenericAnimal
, который его реализует, вместе с Cat
реализующим его.
Скажем, у вас есть объект, представляющий зоопарк, и в большинстве зоопарков обычно есть коллекция животных. Наиболее очевидный способ представить это было бы так:
java.util.Collection<com.myproject.Animal> zooAnimals;
Итак, теперь представьте, что зоопарк строит новую среду обитания, и это для льва. Ради истории предположим, что у нас очень ленивая модель данных, и вместо того, чтобы иметь конкретный подтип c com.myproject.animals.cats.Lion
, мы просто сказали: «львы - это кошки, достаточно близкие». Поэтому, чтобы обновить структуру данных, которая отслеживает всех животных, их имена, адреса, любимые продукты и все остальное, мы могли бы сделать это:
com.myproject.Animal newArrival = new com.myproject.animals.Cat("Larry the Lion", "Africa Exhibit", "Gazelles");
zooAnimals.add(newArrival);
Теперь представьте, что зоопарк продолжает расти и получает страуса. в среде обитания Африки. И та же самая ленивая модель данных применима, поэтому мы просто называем это Bird
.
com.myproject.Animal newArrival = new com.myproject.animals.Bird("Oliver the Ostrich", "Africa Exhibit", "Whatever Ostriches Eat");
zooAnimals.add(newArrival);
Теперь на самом деле написание этого точного кода обычно происходит только в очень специфических c случаях внутри объекта фабрики или чего-то подобного, и реалистично подобранные иерархии типов имеют тенденцию вообще не очень хорошо работать, вопреки тому, что многие из нас узнали в классе объектно-ориентированного программирования, но ради вопроса, который является примером ситуации, когда вы могли бы сделать то, что Вы спрашиваете о.
Наконец, вы также спросили, когда вам нужно сыграть. Вы должны были бы сделать это, если бы у вас был код, который должен был знать о любых специальных методах или полях, которые есть у типов Cat
или Bird
, которых нет у Animal
. Например, тип Cat
может иметь свойство, называемое tailLength
, потому что у кошек обычно есть хвосты, и по какой-то причине зоопарк любит отслеживать это. Точно так же тип Bird
может иметь свойство, называемое wingSpan
, потому что у птиц есть крылья, и мы хотим отслеживать, насколько они велики. Тип Animal не имеет ни одного из этих свойств, поэтому, если мы получим объект для льва или страуса из коллекции zooAnimals (и, возможно, мы посмотрели на имя или что-то еще, чтобы выяснить, что это был лев), нам бы пришлось приведите обратно к типу Cat
, чтобы получить доступ к свойству tailLength
. То же самое для страуса и его размах крыльев.
for( Animal theAnimal : zooAnimals ){
if( theAnimal.getName().equals("Larry the Lion") ){
Cat theCat = (Cat)theAnimal;
System.out.println("Larry's tail is " + theCat.getTailLength() + " inches long";
}
else if( theAnimal.getName().equals("Oliver the Ostrich") ){
Bird theBird = (Bird)theAnimal;
System.out.println("Oliver's wingspan is " + theBird.getWingSpan() + " inches";
}
}
Опять же, вы, вероятно, на самом деле не сделаете что-то подобное в реальном коде, но, возможно, это поможет проиллюстрировать пример.