1. Приведение типов

Переменные ссылочных типов (классов) тоже можно преобразовывать к разным типам. Однако это работает только в рамках одной иерархии типов. Давайте рассмотрим на простом примере. Допустим, у нас есть такая иерархия классов: классы ниже наследуются от классов выше.

Приведение типов

Приведение ссылочных типов, как и примитивных тоже делится на расширяющие и сужающее.

Мы видим, что класс Кот унаследован от класса ДомашнееЖивотное, а класс ДомашнееЖивотное в свою очередь от класса Животное.

Если мы напишем такой код:

Животное котик = new Кот();

Это расширяющее приведение типа: его еще называют неявным. Мы расширили ссылку котик, и теперь она ссылается на объект типа Кот. При таком приведении мы не сможем через ссылку котик вызвать методы, которые есть у класса Кот, но которых нет у класса Животное.

Сужающее приведение (или явное) происходит в обратную сторону:

Кот котэ = (Кот) котик;

Мы явно указали, что хотим привести ссылку, которая хранится в переменной котик (типа Животное) к типу Кот.


18
Задача
Java Syntax PRO Comics,  18 уровень4 лекция
Недоступна
Космическая одиссея ч.1
Перед тем, как космический корабль отправится бороздить просторы Вселенной, необходимо пригласить на борт экипаж, который будет состоять из 2 людей, 1 собаки и 1 кота. В методе createCrew() добавь необходимое количество экземпляров соответствующих классов в список astronauts. Подсказка: Чтобы добави
18
Задача
Java Syntax PRO Comics,  18 уровень4 лекция
Недоступна
Космическая одиссея ч.2
Все члены экипажа космического корабля находятся на борту, корабль успешно вышел в открытый космос. Команде пора приступить к выполнению своих обязанностей: Людям — пилотировать корабль, Собаке — заниматься навигацией, а Коту — исследовать открытый космос. Распредели обязанности членов экипажа в ме

2. Проверка типа объекта

Но тут нужно быть очень осторожным. Если вы сделаете так:

Животное зверь = new Кот();
Волк серыйВолк = (Волк) зверь;

Компилятор пропустит этот код, а вот во время выполнения программы возникнет ошибка! JVM кинет вам исключение:

Exception in thread "main" java.lang.ClassCastException: Кот cannot be cast to Волк

Ссылку на объект Кот можно сохранить только в переменные, которые имеют тип класса-родителя для класса Кот: ДомашнееЖивотное, Животное и Object.

Почему же так?

Все дело в том, что ссылка на объект используется для того, чтобы обращаться к методам и переменным этого объекта. И если мы сохраним в переменную типа Животное ссылку на объект Кот, то никаких проблем с этим не возникнет: у типа Кот всегда будет переменная и методы типа Животное: он же их унаследовал!

А вот если бы JVM разрешила сохранить ссылку на объект Кот в переменную типа Волк, могла бы возникнуть ситуация, когда у переменной серыйВолк вызывается метод, который отсутствует у объекта Кот, на который эта переменная и ссылается. Поэтому такое сохранение не разрешается.

В Java есть специальный оператор — instanceof, который позволяет проверить, можно ли сохранить объект определенного типа в переменную определенного типа. Выглядит он достаточно просто:

переменная instanceof Тип

Пример:

Животное зверь = new Кот();
if (зверь instanceof Волк)
{
   Волк серыйВолк = (Волк) зверь;
}

Такой код не вызовет ошибок даже во время выполнения.

Вот еще несколько примеров с описанием ситуации:

Расширение типа Описание
Cow cow = new Whale();

Классическое расширение типа — оператор преобразования типа не требуется. Теперь у объекта типа Whale можно вызывать только методы, описанные в классе Cow.

Компилятор разрешит вызвать у переменной cow только те методы, которые есть у ее типа — класса Cow.

Сужение типа
Cow cow = new Whale();
if (cow instanceof Whale)
{
   Whale whale = (Whale) cow;
}
Классическое сужение типа: нужно добавить проверку типа и оператор приведения типа.
Переменная cow типа Cow хранит ссылку на объект класса Whale.
Мы проверяем, что это так и есть, и затем выполняем операцию преобразования (сужение) типа. Или как ее еще называют —
type cast
.

Cow cow = new Cow();
Whale whale = (Whale) cow; // exception
Ссылочное сужение типа можно провести и без проверки типа объекта.
При этом, если в переменной cow хранился объект не класса Whale, будет сгенерировано исключение — InvalidClassCastException.

18
Задача
Java Syntax PRO Comics,  18 уровень4 лекция
Недоступна
Космическая одиссея ч.3
Сделаем наш предыдущий пример более правильным и универсальным. Порядок, в котором добавляются астронавты в список astronauts, не всегда будет известен. Чтобы правильно распределить задачи членам экипажа, необходимо определить, кем является астронавт. Для этого в методе runWorkingProcess() перебери
18
Задача
Java Syntax PRO Comics,  18 уровень4 лекция
Недоступна
Хищники vs Травоядные
Перед тобой 4 класса животных — Cow, Lion, Wolf и Elephant. Корова (Cow) и слон (Elephant) являются травоядными, поэтому они наследуются от класса Herbivore. А лев (Lion) и волк (Wolf) являются хищниками, поэтому они наследуются от класса Predator. В свою очередь классы Herbivore и Predator унаследо

3. Вызов оригинального метода: super

Иногда бывает нужно не заменить метод родительского класса на свой при переопределении метода, а лишь немного дополнить его.

Было бы классно, если бы мы могли в нашем методе вызвать такой же метод родительского класса, а потом еще выполнить какой-то свой код. Ну или сначала выполнить свой код, а потом вызвать метод родительского класса.

И такая возможность в Java есть. Вызов метода именно родительского класса делает так:

super.метод(параметры);

Примеры:

class МирноеВремя
{
   public double getPi()
   {
      return 3.14;
   }
}

class ВоенноеВремя extends МирноеВремя
{
   public double getPi()
   {
      return super.getPi()*2;  // 3.14*2
   }
}

В военное время значение Pi может достигать четырех, а в нашем случае вообще 6! Это, конечно, шутка, но она демонстрирует, как это все может работать.

Вот еще пара примеров, чтобы немного прояснить ситуацию:

Код Описание
class Cow
{
   public void printAll()
   {
      printColor();
      printName();
   }

   public void printColor()
   {
      System.out.println("Я — белый");
   }

   public void printName()
   {
      System.out.println("Я — корова");
   }
}

class Whale extends Cow
{
   public void printName()
   {
      System.out.print("Это неправда: ");
      super.printName();
      System.out.println("Я — кит");
   }
}
Классы Cow и Whale
public static void main(String[] args)
{
   Whale whale = new Whale();
   whale.printAll();
}
На экран будет выведена надпись:
Я — белый
Это неправда: Я — корова
Я — кит

Да, это непростой материал: честно говоря, он один из самых сложных в ООП. Однако знать и понимать его необходимо.


18
Задача
Java Syntax PRO Comics,  18 уровень4 лекция
Недоступна
Питомцы бывают разные
Класс Pet является родительским классом для классов Cat и Dog. В нем реализован метод printInfo(), который сообщает, что данный объект является питомцем. В классах Cat и Dog переопредели метод printInfo(), дополнив его функционал следующим образом: - вначале вызови метод printInfo() родительского кл