JavaRush/Java блог/Java Developer/instanceof и основы наследования
Автор
Aditi Nawghare
Инженер-программист в Siemens

instanceof и основы наследования

Статья из группы Java Developer
участников
Привет! В предыдущих лекциях мы уже несколько раз мельком встречались с таким понятием как наследование. Сегодня мы тоже коснемся этой темы, но тоже не слишком глубоко. Подробная лекция об этом еще будет, а сегодня скорее просто посмотрим практические примеры и познакомимся с одним интересным оператором в Java.

Наследование Java

Итак, что такое, собственно, наследование? instanceof и основы наследования - 1Наследование — это механизм в программировании, в том числе и в Java, который позволяет описать новый класс на основе уже существующего. Класс-наследник при этом получает доступ к полям и методам родительского класса. Зачем это может быть нужно? Ну, например, представь, что тебе нужно создать в программе несколько классов машин: Грузовик, Гоночная, Седан, Пикап и т.д. Даже не приступив к написанию кода, ты точно знаешь, что у этих классов очень много общего: у всех машин есть название модели, год выпуска, объем двигателя, максимальная скорость и т.д. (не говоря уже про то, что у всех них есть колеса и прочие детали). В такой ситуации ты можешь:
  • Создавать эти поля в каждом классе и добавлять их в новые классы машин при их создании
  • Вынести общие для всех машин поля в родительский класс Car, а все классы конкретных типов машин наследовать от Car с помощью слова extends.
Второй вариант, разумеется, гораздо удобнее:
public class Car {

   private String model;
   private int maxSpeed;
   private int yearOfManufacture;

   public Car(String model, int maxSpeed, int yearOfManufacture) {
       this.model = model;
       this.maxSpeed = maxSpeed;
       this.yearOfManufacture = yearOfManufacture;
   }
}

public class Truck extends Car {

   public Truck(String model, int maxSpeed, int yearOfManufacture) {
       super(model, maxSpeed, yearOfManufacture);
   }
}

public class Sedan extends Car {
   public Sedan(String model, int maxSpeed, int yearOfManufacture) {
       super(model, maxSpeed, yearOfManufacture);
   }
}
Как минимум, мы избежали ненужного дублирования кода, а к этому всегда нужно стремиться при написании программ. Кроме того, у нас есть простая и понятная структура классов: общие для всех машин поля вынесены в один класс. Если, например, у грузовиков есть какие-то специфичные поля, которых нет у остальных машин, их можно объявить в классе Truck. То же самое касается и методов. У всех автомобилей есть какое-то общее поведение, которое можно описать: завести авто, газ/тормоз и т.д. Эти общие методы можно вынести в общий класс Car, а специфическое поведения каждого конкретного типа описать в классах-наследниках.
public class Car {

   public void gas() {
       //...газ
   }

   public void brake() {
       //...тормоз
   }
}


public class F1Car extends Car {

   public void pitStop() {

       //...пит-стоп делают только гоночные автомобили
   }

   public static void main(String[] args) {

       F1Car formula1Car = new F1Car();
       formula1Car.gas();
       formula1Car.pitStop();
       formula1Car.brake();
   }
}
Мы вынесли общие методы всех автомобилей в класс Car. А вот в класс-наследник F1Car, который описывает гоночные автомобили “Формулы-1” — пит-стопы (остановки для срочного обслуживания машины), которые делают только в гонках и выделяются специфическим поведением.

Оператор instanceof Java

Для проверки того, создан ли объект на основе какого-то класса, в Java существует специальный оператор — instanceof. Он возвращает значение true, если проверка показала истинность, или false, если результат был ложным. Давай посмотрим, как он работает на примере наших классов с машинами:
public class Truck extends Car {

   public static void main(String[] args) {

       Truck truck = new Truck();
       System.out.println(truck instanceof Car);
   }
}
Вывод: true Проверка с помощью оператора instanceof вернула true, поскольку у нас объект класса Truck, а все грузовики — это машины. Класс Truck— наследник класса Car, следовательно, все грузовики создаются на основе общего родителя — машины. Обрати внимание на оператор instanceof: он пишется без точки, поскольку это именно оператор, а не метод (“объект instanceof Класс”). Попробуем по-другому:
public static void main(String[] args) {

   Car car = new Car();
   System.out.println(car instanceof Truck);
}
Вывод: false Класс Car и, соответственно, его объект не происходят от класса Truck.Все грузовики — это машины, но не все машины — грузовики. Объекты Car не создаются на основе класса Truck. Еще один пример:
public static void main(String[] args) {

   Car car = new Car();
   Truck truck = new Truck();
   System.out.println(car instanceof Object && truck instanceof Object);
}
Вывод: True Здесь логика тоже проста: все классы в Java, включая те, которые ты создал, происходит от класса Object (хотя ты и не пишешь в них extends Object — этот механизм заложен в них неявно). Зачем это может пригодиться и при каких обстоятельствах? Наиболее распространенное применение оператора instanceof — это переопределение метода equals(). К примеру, вот как реализован метод equals в классе String:
public boolean equals(Object anObject) {
   if (this == anObject) {
       return true;
   }
   if (anObject instanceof String) {
       String anotherString = (String) anObject;
       int n = value.length;
       if (n == anotherString.value.length) {
           char v1[] = value;
           char v2[] = anotherString.value;
           int i = 0;
           while (n-- != 0) {
               if (v1[i] != v2[i])
                       return false;
               i++;
           }
           return true;
       }
   }
   return false;
}
Прежде чем сравнить строку с переданным объектом, метод проверяет: а является ли, собственно, переданный объект строкой? И уж только потом он начинает сравнивать свойства двух объектов. Без этой проверки в метод можно было бы передать любой объект, у которого есть поля value и length, и сравнивать его со строкой, что, конечно, было бы неправильно.
Комментарии (153)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Sasha Rozanov
Уровень 32
20 апреля 2023, 08:38
Привет всем читающим :) value это приватное статичное поле класса String, ей присвоено значение объекта String. String text = "123"; String text2 = "456"; text.equals(text2); // "123" это value
Kirill
Уровень 18
22 ноября 2021, 23:18
Про реализацию метода equals в классе String: Для пояснений можно взять следующий код: (можно в IDE его скопировать, чтоб посмотреть вывод)
String first = "привет";
String linkToFirst = first;
String second = new String ("привет");
System.out.println(first.equals(linkToFirst));
System.out.println(first.equals(second));
Первый System.out.println выведет true. И дойдет проверка только до первого if. Мы вызываем у объекта "first", типа String, метод equals, передавая в качестве параметра другую строку "linkToFirst", которая, как мы видим, просто ссылается на тот же самый объект. Оператор "this" ссылается на объект, который вызвал этот метод. В нашем случае - это "first" вызвал метод "equals", и в качестве передаваемого параметра отправил туда second. Когда мы написали в коде this, то подразумевали - "first". Т.е. Это можно прочитать как:
public boolean equals(Object anObject) {
   if (first == second) {
Так почему же мы попадем только в первый if? Потому что при использовании оператора == при сравнении объектов, сравниваются не сами объекты, а только ссылки на них. А у нас как раз такая история - мы ссылаемся на один и тот же объект, получаем return true, и вывод в консоль true.
public boolean equals(Object anObject) {
   if (this == anObject) {
       return true;
   }
Не дает всё сразу опубликовать, поэтому второй System.out.println(first.equals(second)); ниже
Kirill
Уровень 18
22 ноября 2021, 23:17
Второй System.out.println выведет также true, но проверка уже пойдет по всему методу, ибо первый if выдаст false. Ведь second не ссылается на first привет, а ссылается на новый объект
new String
строки "привет".
if (anObject instanceof String) {  //Проверяем, является ли строкой,
// переданный в метод объект (second)
       String anotherString = (String) anObject; //если да, то приводим
// переданный объект к типу String
       int n = value.length;  // value - это массив символов*, объекта String,
// который вызывает метод equals.
// value.length - его длина. *- на самом деле это массив байтов, и каждый
// символ может быть представлен в виде 1, 2, 3 или даже 4 байтами,
// но сути это не меняет
       if (n == anotherString.value.length) { // сравниваем длину массива
// символов текущего объекта, с переданным т.е. длину first и second)
           char v1[] = value;
           char v2[] = anotherString.value;
           int i = 0;
           while (n-- != 0) {  // далее мы в цикле уменьшаем количество
// заходов в while и поочередно сравниваем между собой элементы (символы)
// массивов исходного "first " и  переданного
// "second" (берется один и тот же индекс массива - "i").
// Как только они не совпадут - false и выход из цикла.
// Если цикл завершился без преждевременного окончания,
// значит объекты идентичны.
               if (v1[i] != v2[i])
                       return false;
               i++;
           }
           return true;
       }
   }
   return false;
В общем на ваш суд, если что-то непонятно или некорректно - поправлю.
fFamous
Уровень 51
27 июля 2021, 13:41
Как я и думал, value: /** The value is used for character storage. */ private final char value[]; http://hg.openjdk.java.net/jdk8/jdk8/jdk/file/687fd7c7986d/src/share/classes/java/lang/String.java Но насколько я понял, в последних версиях java хранит String не в массиве char, а в байтах.
Wiun
Уровень 16
18 апреля 2021, 10:44
if (this == anObject)
что здесь обозначает this?
Людмила
Уровень 20
19 апреля 2021, 09:53
this - объект, у которого вызывается метод. Например: a.equals(b) - внутри метода будет выглядеть как - if (a == b) return true;
Wiun
Уровень 16
19 апреля 2021, 15:26
а можно для тупых как я разжевать? что конкретно тут происходит?
public boolean equals(Object anObject) {
   if (this == anObject) {
       return true;
   }
сравнивается непонятное мне this с каким-то объектом? ЗЫ перечитал на след день с утра) вроде прояснилось 😅😅😅
Людмила
Уровень 20
20 апреля 2021, 10:41
На всяк случай оставлю здесь: "При вызове метода в виде «объект» точка «имя метода», на самом деле вызывается метод класса, в который первым аргументом передаётся тот самый объект. Внутри метода он получает имя this. Именно с ним и его данными происходят все действия." - цитата из этой лекции И про метод equals и Object ещё можно почитать тут! Успехов😉
Lik Java Developer
6 мая 2021, 17:07
Предполагаю, что так: public boolean equals(Object anObject) { ---> сравниваем объекты this и anObject if (this == anObject) { --->если объект А == объекту В return true; ---> если да, то возврат результата }
Alikhan
Уровень 20
31 мая 2021, 12:44
while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } а откуда переменная i в массиве v1[i] и так же в v2? Ведь ее не создавали до цикла while или внутри цикла, до условного оператора if? Ведь комплятор не читает сверху вниз?:
Wiun
Уровень 16
2 июня 2021, 15:28
она создана сразу перед циклом int i = 0;
Лаптевидный
Уровень 19
24 июля 2021, 08:20
Как я понял, данная часть кода позволяет сравнить объект с самим собой. Видимо, в некоторых случаях это нужно и, чтобы не выполнять всю часть кода ниже и не занимать память и время, была написана простая проверка в самом начале.
Стас
Уровень 20
15 марта 2021, 16:09
Последний пример я вообще не понял. Дошел до строки N6 , а дальше мрак.
Redya Daniel
Уровень 16
16 марта 2021, 13:32
Действительно, тут не уточняется что такое "value", но предпологаю, что "value" - это массив символов из которых состоит строка. то есть сторка "Java" массив value будет {'J', 'a', 'v', 'a'}. Исходя из этого попробуйте разобраться в последнем примере еще раз!
Вадим
Уровень 23
20 мая 2021, 16:49
Дальше идет сравнения длин строк (грубо говоря подсчет букв в строке) И если они совпадают, то побуквенное сравнение в цикле. Цикл согласен организован достаточно извращенно, но если вдуматься то все ок.
Эндер Посол с красной планеты в четвёртом измерении
16 января 2021, 10:00
А в чём смысл первого примера кода? Ведь приватные члены класса не наследуется! Это наивысший уровень инкапсуляции!
Anonymous #2497433
Уровень 35
7 февраля 2021, 14:38
в данном случае, мы не обращаемся напрямую к приватным полям класса, мы получаем к ним доступ через публичный конструктор, с ним как раз и работаем, а он в свою очередь работает с полями своего класса. если бы и конструктор был приватным, то да - не получилось бы ни создать объект, ни обратиться к переменным, так как они были бы доступны лишь в пределах своего класса
🦔 Виктор веду учебный тг-канал в t.me/Javangelion Expert
10 ноября 2020, 13:02
Всё хорошо, кроме последнего примера, остаётся ощущение недосказанности, расписали бы его подробнее с комментариями каждого действия. Но, в любом случае, спасибо за труды!
Равиль Ганиев
Уровень 27
6 ноября 2020, 03:28
нашел ответ на свой вопрос!!! Оператор instanceof нельзя использовать, если нет связи между сравниваемым объектом и типом, с которым он сравнивается
Равиль Ганиев
Уровень 27
6 ноября 2020, 03:08
у меня вопрос! сравниваю ( car instanceof Cat) выдает такую ошибку: "Incompatible conditional operand types" почему? прчему не выдает false?
Andrey Java Developer в Инфотранс
25 февраля 2021, 10:19
car - написано с маленькой буквы, возможно у вас класс Car
Тарас Шкарапут
Уровень 17
17 апреля 2021, 15:42
Потому что в вашем примере ( car instanceof Cat) проверяется принадлежит ли car классу Cat. Машина - явно не Кот))) *вероятно просто опечатка)