Ранее мы использовали классы для создания новых типов и использовали наследование для облегчения процесса создания классов. В этой лекции я углублюсь в типы и представлю концепцию полиморфизма.
проверка типа
Любые переменные и ссылки в Java можно использовать только после передачи объявления типа. Ранее мы видели, что данные объекта, данные класса, параметры метода, возвращаемые значения метода и автоматические переменные внутри методов — все они должны объявлять свои типы. Java — строго типизированный язык, который проверяет типы. Если мы используем неправильный тип, это приведет к ошибкам.
Тип не соответствует, привлекательность недействительна
Например, в приведенном ниже классе Test мы присваиваем объект класса Cup ссылке на класс Person:
public class Test { public static void main(String[] args) { Human aPerson; aPerson = new Cup() }}class Human { /** * конструктор */ public Human(int h) { this.height = h; } /** * аксессор */ public int getHeight() { return this.height; } /** * мутатор */ public void GroveHeight(int h) { this.height = this.height + h } Private int height;} class Cup { public void addWater(int w) { this.water = this.water + w } public void DrinkWater (int w) { this.water = this.water - w } Private int Water = 0; ;}
javac вернет:
найдено: Cuprequired: Human aPerson = new Cup() ^1 ошибка;
Базовое преобразование типов
Java может выполнять преобразование типов переменных базовых типов. Различные базовые типы имеют разную длину и диапазон хранения. Если мы преобразуем тип с высокой точностью в тип с низкой точностью, например, конвертируем из float в int, мы можем потерять информацию. Такое преобразование называется сужающим преобразованием. В этом случае нам нужно явно объявить преобразование типов, например:
public class Test { public static void main(String[] args) { int a; a = (int) 1.23; // сужающее преобразование System.out.println(a }};
Если мы преобразуем тип с низкой точностью в тип с высокой точностью, можно не беспокоиться о потере информации. Такое преобразование называется расширяющим преобразованием. Нам не нужно явно требовать преобразования типов, Java может сделать это автоматически:
public class Test { public static void main(String[] args) { int a = 3; b = a; // расширяющее преобразование System.out.println(a);
Базовое преобразование типов
преобразование вверх и полиморфизм
В Java ссылки также могут быть приведены к типу, но есть ограничения.
Мы можем преобразовать ссылку на производный класс в ссылку на его базовый класс, что называется повышающим или расслабленным преобразованием. Следующий класс BrokenCup наследует класс Cup и переопределяет исходные методы addWater() и DrinkWater() в классе Cup:
public class Test { public static void main(String[] args) { Cup aCup; BrokenCup aBrokenCup = new BrokenCup(); // upcast aCup.addWater(10) // привязка метода }}class Cup { public void addWater(int w) { this.water = this.water + w } public void DrinkWater(int w) { this.water = this.water - w; } частная int вода = 0;} класс BrokenCup расширяет Cup {общественный недействительный addWater (int w) { System.out.println ("дерьмо, разбитая чашка" } общественный недействительный DrinkWater (int w) { System.out. println("ом...номер..., внутри нет воды" }};
Результаты запуска программы:
черт, разбитая чашка
Как вы можете видеть выше, без каких-либо явных инструкций мы присваиваем ссылку на производный класс aBrokenCup ссылке на его базовый класс aCup. Преобразование типов будет выполнено автоматически Java.
Затем мы вызвали метод addWater() объекта aCup (который, как мы объявили, имеет тип Cup). Хотя aCup является ссылкой типа Cup, на самом деле она вызывает метод addWater() класса BrokenCup! Другими словами, даже если мы ослабим ссылочный тип на его базовый класс посредством приведения, Java все равно сможет правильно определить тип самого объекта и вызвать правильный метод. Java может определить истинный тип объекта в зависимости от текущей ситуации. Это называется полиморфизмом. Полиморфизм — важный аспект объектно-ориентированного подхода.
Полиморфизм — это механизм, поддерживаемый Java, а также важная объектно-ориентированная концепция. Это поднимает таксономический вопрос о том, действительно ли объекты подкласса «являются» объектами родительского класса. Например, птица тоже животное, машина тоже должна быть средством передвижения. Java сообщает нам, что объект производного класса может использоваться в качестве объекта базового класса, и Java правильно справится с этой ситуацией.
Например, следующие отношения наследования:
Можно сказать, что мы пьем воду из чашки. Фактически конкретный смысл действия питьевой воды сильно изменится в производном классе. Например, питье воды через соломинку и питье воды из разбитой чашки — это очень разные вещи, хотя мы все говорим о «питьевой воде» абстрактно. Конечно, мы можем программировать отдельно для каждого производного класса и вызывать разные методы DrinkWater. Однако, как программисты, мы можем запрограммировать чашку и вызвать метод DrinkWater() класса Cup, независимо от того, какой производной чашкой является чашка. Java вызовет соответствующий правильный метод, как мы видим в приведенной выше программе.
Рассмотрим более содержательный пример: мы добавляем метод Drink() в класс Human. Этот метод получает в качестве параметров объект чашки и целое число. Целые числа обозначают количество выпитой воды:
общественный класс Test { public static void main(String[] args) {Человеческий гость = новый BrokenCup hisCup = новый BrokenCup(); , int w) { aCup.drinkWater(w) }}
Результаты запуска программы:
блин, внутри нет воды
В определении Drink() класса Human мы требуем, чтобы первый параметр был ссылкой типа Cup. Но в реальном приложении (класс Test) используется объект производного класса BrokenCup Cup. Фактически это преобразует hisCup в класс Cup и передает его методу Drink(). В методе мы вызвали метод DrinkWater(). Java обнаружила, что этот объект на самом деле является объектом BrokenCup, поэтому вызвала соответствующий метод BrokenCup.
подавленный
Мы можем понизить ссылку базового класса до ссылки на производный класс, но объект, на который указывает ссылка на базовый класс, уже является объектом производного класса, который нужно понизить. Например, указанный выше hisCup можно преобразовать вверх в ссылку на класс Cup, а затем преобразовать вниз в ссылку на класс BrokenCup.
Тип объекта
В Java все классы фактически имеют общего предка-наследника — класс Object. Класс Object предоставляет несколько методов, например toString(). Мы можем переопределить эти методы в нашем собственном определении класса.
Объект: предок
Мы можем написать программу, которая оперирует объектами Object, и передавать в программу любой объект через upcast.
Я углублюсь в класс Object позже.
(Реализация полиморфизма опирается на поддержку RTTI. Более подробно я расскажу об этом позже.)
Подвести итог
Базовое преобразование типов
полиморфизм
подавленный
Объект