Вы достигли нового уровня

Уровень 14

1. instanceof - что это такое и зачем нужно

- Привет, Амиго! Ты уже раньше знакомился с оператором instanceof. Сегодня я расскажу тебе, как и где его можно использовать. instanceof – это очень простой и эффективный в использовании оператор.

- Звучит, как реклама!

- Он, и правда, очень простой. Он используется в виде: «объект» instanceof «класс».

Он проверяет, является ли объект объектом определенного класса. Все еще проще, чем я говорю. Смотри пример:

КодОписание
Object o = new Integer(3);
boolean isInt = o instanceof Integer;
isInt будет равно true. Объект, на который ссылается переменная o, является объектом класса Integer.
Object o = "Mama";
boolean isInt = o instanceof Integer;
isInt будет равно false. Объект, на который ссылается переменная o, не является объектом класса Integer, он является объектом класса String.
InputStream is = new FileInputStream("");
boolean isFIS = is instanceof FileInputStream;
isFIS будет равно true. Объект, на который ссылается переменная o, является объектом класса FileInputStream.

- Да, очень просто.

- Этот оператор учитывает и наследование. Вот смотри.

КодОписание
class Pet
{
}
class Cat extends Pet
{
}
class Tiger extends Cat
{
}
Тут мы видим три объявленных класса: животное, кот и тигр. Кот наследуется от Животного. А Тигр от Кота.
Object o = new Tiger();
boolean isCat = o instanceof Cat;
boolean isTiger = o instanceof Tiger;
boolean isPet = o instanceof Pet;
isCat будет равно true.
isTiger будет равно true.
isPet будет равно true.
Object o = new Pet();
boolean isCat = o instanceof Cat;
boolean isTiger = o instanceof Tiger;
boolean isPet = o instanceof Pet;
isCat будет равно false.
isTiger будет равно false.
isPet будет равно true.

И даже интерфейсы:

КодОписание
interface Moveable
{
}
class Cat
{
}
class TomCat extends Cat implements Moveable
{
}
Создадим два класса: Cat, TomCat и интерфейс Moveable
Cat o = new TomCat();
boolean isCat = o instanceof Cat;
boolean isMoveable = o instanceof Moveable;
boolean isTom = o instanceof TomCat;
isCat будет равно true.
isMoveable будет равно true.
isTom будет равно true.
Cat o = new Cat();
boolean isCat = o instanceof Cat;
boolean isMoveable = o instanceof Moveable;
boolean isTom = o instanceof TomCat;
isCat будет равно true.
isMoveable будет равно false.
isTom будет равно false.

Оператор instanceof имеет вид: a instanceof B.

Другими словами, оператор instanceof вернет значение true, если:

1) переменная а хранит ссылку на объект типа B

2) переменная а хранит ссылку на объект, класс которого унаследован от B

3) переменная а хранит ссылку на объект реализующий интерфейс B

Иначе оператор instanceof вернет значение false.

- Понятно. А зачем это нужно, дядя Риша?

- Об этом сегодня тебе расскажет Элли. Это очень хороший оператор. Сегодня ты в этом убедишься.

2. Задачи на instanceof

- Привет, Амиго! Ты отличный друг! Профессор сказал придумать тебе 10 задач, но я дам тебе только две. Это максимум, что можно придумать с бодуна. Жалко, тебя вчера не было на вечеринке в твою честь. Отлично оттянулся.

Задачи
1. Bingo

Исправь строчку 'Object o = new Pet();' в методе main так, чтобы программа вывела "Bingo!"
2. Bingo-2

Исправь строчку 'Cat o = new Cat();' так, чтобы программа вывела "Bingo!"

3. Приведение типов. Расширение и сужение

- Привет, Амиго! Тема сегодняшней лекции – расширение и сужение типов. С расширением и сужением примитивных типов ты познакомился уже давно. На 10 уровне. Сегодня мы расскажем, как это работает для ссылочных типов, т.е. для объектов классов.

Тут все довольно просто на самом деле. Представь себе цепочку наследования класса: класс, его родитель, родитель родителя и т.д. до самого класса Object. Т.к. класс содержит все методы класса, от которого он был унаследован, то объект этого класса можно сохранить в переменную любого из его типов родителей.

Пример:

КодОписание
class Pet
{
 public void doPetActions();
}

class Cat extends Pet
{
 public void doCatActions();
}

class Tiger extends Cat
{
 public void doTigerActions();
}
Тут мы видим три объявленных класса: животное, кот и тигр. Кот наследуется от Животного. А Тигр от Кота.
public static void main(String[] args)
{
 Tiger tiger = new Tiger();
 Cat cat = new Tiger();
 Pet pet = new Tiger();
 Object obj = new Tiger();

}
Объект класса Tiger всегда можно спокойно присвоить переменной с типом класса-родителя. Для класса Tiger – это Cat, Pat и Object.

Теперь рассмотрим, что же такое расширение и сужение типов.

Если в результате присваивания мы двигаемся по цепочке наследования вверх (к типу Object), то это сужение типа, а если вниз, к типу объекта, то это расширение типа.

Движение вверх по цепочке наследования называется сужением, т.к. теряется возможность вызвать методы, которые были добавлены в класс при наследовании.

КодОписание
public static void main(String[] args)
{
 Object obj = new Tiger();
 Pet pet = (Pet) obj;
 Cat cat = (Cat) obj;
 Tiger tiger = (Tiger) pet;
 Tiger tiger2 = (Tiger) cat;

}
При расширении типа, нужно использовать оператор преобразования типа.

При этом Java-машина выполняет проверку, а действительно ли данный объект унаследован от Типа, к которому мы хотим его преобразовать.

Такое небольшое нововведение уменьшило количество ошибок в преобразовании типов в разы, и существенно повысило стабильность работы Java-программ.
public static void main(String[] args)
{
 Object obj = new Tiger();
 if (obj instanceof Cat)
 {
  Cat cat = (Cat) obj;
  cat.doCatActions();
 }

}
Еще лучше – использовать проверку instanceof
public static void main(String[] args)
{
 Pet pet = new Tiger();
 doAllAction(pet);

 Pet pet2 = new Cat();
 doAllAction(pet2);

 Pet pet3 = new Pet();
 doAllAction(pet3);
}

public static void doAllAction(Pet pet)
{
 if (pet instanceof Tiger)
 {
  Tiger tiger = (Tiger) pet;
  tiger.doTigerActions();
 }

 if (pet instanceof Cat)
 {
  Cat cat = (Cat) pet;
  cat.doCatActions();
 }

 pet.doPetActions();
}
И вот почему. Смотрим на пример слева.

Мы (наш код) не всегда знаем, с объектом какого типа мы работаем. Это может быть как объект того же типа, что и переменная (Pet), так и любой тип-наследник (Cat, Tiger).

Рассмотрим метод doAllAction. Он корректно работает в независимости от того, объект какого типа в него передали.

Т.е. он корректно работает для всех трех типов Pet, Cat, Tiger.
public static void main(String[] args)
{
 Cat cat = new Tiger();
 Pet pet = cat;
 Object obj = cat;

}
Тут мы видим три присваивания. Все они являются примерами сужения типа.

Оператор преобразования типа тут не нужен, т.к. не нужна проверка. Ссылку на объект всегда можно сохранить в переменную любого его базового типа.

- О, на предпоследнем примере все стало понятно. И для чего нужна проверка, и для чего нужно преобразование типов.

- Надеюсь, что так. Хочу обратить твое внимание на следующую вещь:

С объектом при таком присваивании ничего не происходит! Меняется только количество методов, которое можно вызвать с помощью конкретной переменной-ссылки.

Например, переменная класса Cat позволяет вызывать методы doPetActions & doCatActions, и ничего не знает о методе doTigerActions, даже если ссылается на объект класса Tiger.

- Ну, это-то уже ясно. Оказалось легче, чем я думал.

4. Задачи на приведение типов

- Знаешь, что я думаю?

- Что, друг Диего?

- Ты уже почти катишь на робота моего уровня. Скоро сможешь сам писать программы.

- Серьезно, Диего?

- Конечно же – нет, щенок. Ха-ха. Тебе еще учиться и учиться. Так что не ной и на вот, решай!

Задачи
1. Building и School

1. Расставь правильно наследование между Building(здание) и School(здание школы).
2. Подумай, объект какого класса должны возвращать методы getSchool и getBuilding.
3. Измени null на объект класса Building или School.
2. Коты

1. Считывать строки(параметры) с консоли, пока пользователь не введет пустую строку(Enter).
2. Каждый параметр соответствует имени кота.
Для каждого параметра:
3. Создать объект cat класса Cat, который равен коту из getCatByKey(String параметр).
4. Вывести на экран cat.toString().
3. Food

1. Реализовать интерфейс Selectable в классе Food.
2. Метод onSelect() должен писать в консоль "food is selected".
3. Подумай, какие методы можно вызвать для переменной food и какие для selectable.
4. В методе foodMethods вызови методы onSelect, eat, если это возможно.
5. В методе selectableMethods вызови методы onSelect, eat, если это возможно.
6. Явное приведение типов не использовать.
4. Без ошибок

Инициализировать объект obj таким классом, чтобы метод main выполнился без ошибок.
5. Player and Dancer

1. Подумать, что делает программа.
2. Изменить метод haveRest так, чтобы он вызывал метод
    - play, если person имеет тип Player
    - dance, если person имеет тип Dancer

5. Ссылка на вики, приведение типов и instanceof

- Это самые лучшие источники. Сам дядечка Эккель читал мне эти лекции. Они могут показаться немного сложными для понимания, но польза от них бесценная.

Ссылка на вики, приведение типов и instanceof

6. Домашние и бонусные задания

- Ты что, так быстро решил то, что я тебе давал?! Нельзя просто так взять и решить все мои задачи. Мне кажется – ты халтуришь. Вот тебе новое задание, это очень сложное, будь внимателен и разбери все по полочкам, почитай литературу, поспрашивай у друзей! Никто не смеет обманывать Великого Диего!

Задачи
Куриная фабрика

Написать Фабрику(Factory) по производству кур(Hen)
1. Создать класс Hen
1.1. Сделать его абстрактным
1.2. Добавить в класс абстрактный метод int getCountOfEggsPerMonth()
1.3. Добавить в класс метод String getDescription(), который возвращает строку "Я курица."

2. Создать класс RussianHen, который наследуется от Hen
3. Создать класс UkrainianHen, который наследуется от Hen
4. Создать класс MoldovanHen, который наследуется от Hen
5. Создать класс BelarusianHen, который наследуется от Hen

6. В каждом из четырех последних классов написать свою реализацию метода getCountOfEggsPerMonth.
Методы должны возвращать количество яиц в месяц от данного типа куриц.

7. В каждом из четырех последних классов написать свою реализацию метода getDescription.
Методы должны возвращать строку вида:
<getDescription() родительского класса> + <" Моя страна - Sssss. Я несу N яиц в месяц.">;
где Sssss - название страны
где N - количество яиц в месяц

8. В классе HenFactory реализовать метод getHen, который возвращает соответствующую стране породу кур
9. Все созданные вами классы должны быть в отдельных файлах

7. Хулио

- Привет, Амиго! Пора немного отдохнуть. Ну что, смотрим видео?

Оригинал видео на YouTube

8. Домашние и бонусные задания

- Здорово, боец!

- Здравия желаю, товарищ капитан!

- У меня для тебя шикарная новость. Вот тебе задания для закрепления полученных навыков. Выполняй их каждый день, и твои навыки будут расти с неимоверной скоростью. Они специально разработаны для выполнения их в Intellij IDEA.

Дополнительные задания для выполнения в Intellij Idea
1. Мосты.

1. Создать интерфейс Bridge с методом int getCarsCount().
2. Создать классы WaterBridge и SuspensionBridge, которые реализуют интерфейс Bridge.
3. Метод getCarsCount() должен возвращать любое значение типа int.
4. Метод getCarsCount должен возвращать различные значения для различных классов.
5. В классе Solution создать публичный метод println(Bridge bridge).
6. В методе println вывести на консоль значение getCarsCount() для объекта bridge.
7. Каждый класс и интерфейс должны быть в отдельных файлах.
2. Дегустация вин.

1. Создать абстрактный класс Drink с реализованным методом void taste(), который выводит в консоль "Вкусно".
2. Создать класс Wine, который наследуется от Drink, с реализованным методом String getHolidayName(), который возвращает строку "День рождения".
3. Создать класс BubblyWine, который наследуется от Wine, с реализованным методом String getHolidayName(), который возвращает строку "Новый год".
4. Написать такую реализацию методов getDeliciousDrink, getWine, getBubblyWine, чтобы метод main отработал без ошибок.
5. Каждый класс и интерфейс должны быть в отдельных файлах.
6. Метод main менять нельзя!
3. User, Looser, Coder and Proger.

1. Ввести [в цикле] с клавиатуры несколько строк (ключей).
Строки(ключи) могут быть такими: "user", "looser", "coder", "proger".
Ввод окончен, когда строка не совпадает ни с одной из вышеуказанных.

2. Для каждой введенной строки нужно:
2.1. Создать соответствующий объект [см Person.java], например, для строки "user" нужно создать объект класса User.
2.2. Передать этот объект в метод doWork.

3. Написать реализацию метода doWork, который:
3.1. Вызывает метод live() у переданного обекта, если этот объект (person) имеет тип User.
3.2. Вызывает метод doNothing(), если person имеет тип Looser.
3.3. Вызывает метод coding(), если person имеет тип Coder.
3.4. Вызывает метод enjoy(), если person имеет тип Proger.
4. Реализовать метод printMainInfo.

1. Напиши реализацию метода printMainInfo, чтобы:
1.1. Если в метод передают объект типа Drawable, у этого объекта вызывался метод draw.
1.2. Если в метод передают объект типа Movable, у этого объекта вызывался метод move.
2. Метод main менять нельзя.
5. Computer.

1. Создай интерфейс CompItem.
2. Добавь в него метод String getName().
3. Создай классы Keyboard, Mouse, Monitor, которые реализуют интерфейс CompItem.
4. Метод getName() должен возвращать имя класса, например, для класса Keyboard будет "Keyboard".
5. Создай класс Computer.
6. В класс Computer добавь приватное поле типа Keyboard.
7. В класс Computer добавь приватное поле типа Mouse.
8. В класс Computer добавь приватное поле типа Monitor.
9. Создай конструктор в классе Computer используя комбинацию клавиш Alt+Insert внутри класса (команда Constructor).
10. Параметрами конструктора выбери все три поля (переменных) класса.
11. Создай геттеры для полей класса Computer (в классе используй комбинацию клавиш Alt+Insert и выбери команду Getter).
12. Все созданные классы и интерфейс должны быть в отдельных файлах.
13. Класс Solution менять нельзя.
6. MovieFactory.

Расширение функционала по аналогии, чтение с консоли:
1. Разобраться, что программа умеет делать.
2. Все классы должны быть внутри класса Solution.
3. Добавить классы Cartoon, Thriller.
4. Разобраться, как мы получаем объект класса SoapOpera по ключу "soapOpera".
Аналогично получению объекта SoapOpera сделать:
5. Добавить в MovieFactory.getMovie получение объекта Cartoon для ключа "cartoon".
6. Добавить в MovieFactory.getMovie получение объекта Thriller для ключа "thriller".

7. Считать с консоли несколько ключей (строк).
7.1. Ввод заканчивается, как только вводится строка не совпадающая с одной из: "cartoon", "thriller", "soapOpera".

8. Создать переменную movie класса Movie и для каждой введенной строки(ключа):
8.1. Получить объект используя MovieFactory.getMovie и присвоить его переменной movie.
8.2. Вывести на экран movie.getClass().getSimpleName().
7. Клининговый центр.

1. Реализовать метод cleanAllApartaments.
Для каждого объекта из apartaments:
2. Для однокомнатных квартир (Apt1Room) вызвать метод clean1Room, т.е. если объект типа Apt1Room, то вызвать у него метод clean1Romm.
3. Для двухкомнатных квартир (Apt2Room) вызвать метод clean2Rooms, т.е. если объект типа Apt2Room, то вызвать у него метод clean2Rooms.
4. Для трехкомнатных квартир (Apt3Room) вызвать метод clean3Rooms, т.е. если объект типа Apt3Room, то вызвать у него метод clean3Rooms.
8. Исправление ошибок.

1. Подумать, как связаны интерфейсы Swimable(способен плавать) и Walkable(способен ходить) с классом OceanAnimal(животное океана).
2. Расставить правильно наследование интерфейсов и класса OceanAnimal.
3. Подумать, как могут быть связаны классы Orca(Косатка), Whale(Кит), Otter(Выдра) с классом OceanAnimal.
4. Расставить правильно наследование между классами Orca, Whale, Otter и классом OceanAnimal.
5. Подумать, какой класс должен реализовать интерфейс Walkable и добавить интерфейc этому классу.
6. Подумать, какое животное еще не умеет плавать и добавить ему интерфейс Swimable.
9. Валюты.

1. Реализуй метод getAmount в классе Money:
1.1. Подумай, какого типа нужно создать приватную переменную, если метод getAmount будет ее возвращать.
1.2. Создай приватную переменную этого типа и верни ее в методе getAmount.
1.3. В конструкторе присвой ей значение, полученное параметром.
2. В отдельном файле создай класс Hrivna.
3. Наследуй класс Hrivna от класса Money.
4. В классе Hrivna реализуй метод getCurrencyName, который возвращает "HRN".
5. В отдельном файле создай класс USD.
6. Наследуй класс USD от класса Money.
7. В классе USD реализуй метод getCurrencyName, который возвращает "USD".
8. Подумай, объекты каких классов можно добавить в список(лист) allMoney.
9. Добавь в конструктор класса Person заполнение листа allMoney объектами всех возможных классов.
10. Исправить 4 ошибки.

Исправить 4 ошибки в конструкторе NotIncapsulatedClass и отрефактори код
1. В класе NotIncapsulatedClass создать private методы initList(List<Number> list), printListValues, processCastedObjects.
2. Метод initList должен заполнять значениями входящий параметр list:
    - найди нужный блок кода в конструкторе, в котором list заполняется значениями
    - перенеси его в метод initList
    - верни заполненный list.
3. Метод printListValues должен принимать параметр list и вывести в консоль все элементы из списка list:
    - метод ничего не возвращает
    - найди нужный блок кода в конструкторе, в котором в цикле из списка list выводятся в консоль все значения
    - перенеси его в метод printListValues
    - исправь 2 ошибки в этом методе.
4. Метод processCastedObjects:
    - входящий параметр метода имеет тип List<Number> list
    - метод ничего не возвращает
    - найди нужный блок кода в конструкторе, в котором в цикле для каждого объекта из списка list проверяется его тип
    - перенеси этот блок в метод processCastedObjects
    - исправь 2 ошибки в этом методе
    - учти, что для объекта типа Float нужно вывести "Is float value defined? " + [Float_object].isNaN()
    - учти, что для объекта типа Double нужно вывести "Is double value infinite? " + [Double_object].isInfinite().

- Те задания были для духов. Для дедушек я добавил бонусные задания повышенной сложности. Только для старослужащих.

1. Нашествие эксепшенов.

Задача: Заполни массив exceptions 10 различными эксепшенами.
Первое исключение уже реализовано в методе initExceptions.
2. НОД.

Задача: Наибольший общий делитель (НОД).
Ввести с клавиатуры 2 целых положительных числа.
Вывести в консоль наибольший общий делитель.
3. Singleton.

Задача: Класс является синглтоном (реализует паттерн(шаблон) Singleton), если позволяет создать всего один объект своего типа.

Реализовать Singleton pattern:
1. Создай класс Singleton в отдельном файле.
2. Добавь в него статический метод getInstance().
3. Метод getInstance должен возвращать один и тот же объект класса Singleton при любом вызове метода getInstance.
4. Подумай, каким образом можно запретить создание других объектов этого класса.
5. Сделай все конструкторы в классе Singleton приватными (private).
6. В итоге должна быть возможность создать объект (экземпляр класса) ТОЛЬКО используя метод getInstance.