Рубрики
Без рубрики

Руководство по наследованию на Java

Узнайте о концепции наследования в Java.

Автор оригинала: baeldung.

1. Обзор

Один из основных принципов объектно-ориентированного программирования – наследование – позволяет нам повторно использовать существующий код или расширить существующий тип.

Проще говоря, в Java класс может унаследовать другой класс и несколько интерфейсов, в то время как интерфейс может унаследовать другие интерфейсы.

В этой статье мы начнем с необходимости наследования, переходя к тому, как наследство работает с классами и интерфейсами.

Затем мы покроем, как имена переменных/методов и модификаторы доступа влияют на унаследованные элементы.

И в конце концов, мы увидим, что значит наследовать тип.

2. Потребность в наследстве

Представьте себе, как производитель автомобилей, вы предлагаете несколько моделей автомобилей для ваших клиентов. Несмотря на то, что различные модели автомобилей могут предложить различные функции, такие как люк или пуленепробиваемые окна, все они будут включать в себя общие компоненты и функции, такие как двигатель и колеса.

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

Аналогичным образом, с наследованием, мы можем создать класс с основными функциями и поведением и создать его специализированные версии, создавая классы, которые наследуют этот базовый класс. Таким же образом интерфейсы могут расширять существующие интерфейсы.

Мы заметим использование нескольких терминов для обозначения типа, который наследуется другим типом, в частности:

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

3. Наследование класса

3.1. Расширение класса

Класс может унаследовать другой класс и определить дополнительных членов.

Начнем с определения базового класса Автомобильные :

public class Car {
    int wheels;
    String model;
    void start() {
        // Check essential parts
    }
}

Классная ArmoredCar может унаследовать членов Автомобильные класс по с помощью ключевого расширяет в своем заявлении :

public class ArmoredCar extends Car {
    int bulletProofWindows;
    void remoteStartCar() {
	// this vehicle can be started by using a remote control
    }
}

Теперь мы можем сказать, что ArmoredCar класс является подклассом Автомобиль, и последний является суперклассом Бронированный автомобиль.

Классы в Java поддерживают единый ; ArmoredCar класс не может расширить несколько классов.

Также обратите внимание, что при отсутствии расширяет ключевое слово, класс неявно наследует класс java.lang.Object .

Класс подкласса наследует не статическое защищенные и общественные из класса суперкласс а. Кроме того, члены с по умолчанию и пакет доступ наследуется, если два класса находятся в одном пакете.

С другой стороны, частные и статические члены класса не наследуются.

3.2. Доступ к родительским членам из класса ребенка

Чтобы получить доступ к унаследованным свойствам или методам, мы можем просто использовать их напрямую:

public class ArmoredCar extends Car {
    public String registerModel() {
        return model;
    }
}

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

4. Наследование интерфейса

4.1. Внедрение нескольких интерфейсов

Хотя классы могут унаследовать только один класс, они могут реализовать несколько интерфейсов.

Представьте себе ArmoredCar что мы определили в предыдущем разделе требуется для супер шпиона. Итак, Автомобильные производственная компания думала о добавлении летающей и плавающей функциональности:

public interface Floatable {
    void floatOnWater();
}
public interface Flyable {
    void fly();
}
public class ArmoredCar extends Car implements Floatable, Flyable{
    public void floatOnWater() {
        System.out.println("I can float!");
    }
 
    public void fly() {
        System.out.println("I can fly!");
    }
}

В приведеном выше примере мы замечаем использование ключевого слова реализует унаследовать от интерфейса.

4.2. Проблемы с множественным наследованием

Java позволяет несколько наследования с помощью интерфейсов.

До Java 7 это не было проблемой. Интерфейсы могут определять только абстрактные методы, то есть методы без какой-либо реализации. Так что, если класс реализовал несколько интерфейсов с одной и той же подписью метода, это не было проблемой. В конечном итоге класс реализации имел только один метод реализации.

Давайте посмотрим, как это простое уравнение изменилось с введением по умолчанию методы в интерфейсах, с Java 8.

Начиная с Java 8, интерфейсы могут определить реализации по умолчанию для своих методов (интерфейс все еще может определить абстрактные методы). Это означает, что если класс реализует несколько интерфейсов, которые определяют методы с одной и той же подписью, класс детей унаследует отдельные реализации. Это звучит сложно и не допускается.

Java не позволяет наследовать несколько реализаций одними и теми же методами, определенными в отдельных интерфейсах.

Вот пример:

public interface Floatable {
    default void repair() {
    	System.out.println("Repairing Floatable object");	
    }
}
public interface Flyable {
    default void repair() {
    	System.out.println("Repairing Flyable object");	
    }
}
public class ArmoredCar extends Car implements Floatable, Flyable {
    // this won't compile
}

Если мы хотим реализовать оба интерфейса, нам придется переопределить ремонт () метод.

Если интерфейсы в предыдущих примерах определяют переменные с одинаковым именем, скажем, продолжительность , мы не можем получить к ним доступ, не предшествуя переменному имени с именем интерфейса:

public interface Floatable {
    int duration = 10;
}
public interface Flyable {
    int duration = 20;
}
public class ArmoredCar extends Car implements Floatable, Flyable {
 
    public void aMethod() {
    	System.out.println(duration); // won't compile
    	System.out.println(Floatable.duration); // outputs 10
    	System.out.println(Flyable.duration); // outputs 20
    }
}

4.3. Интерфейсы, расширяющие другие интерфейсы

Интерфейс может расширить несколько интерфейсов. Вот пример:

public interface Floatable {
    void floatOnWater();
}
interface interface Flyable {
    void fly();
}
public interface SpaceTraveller extends Floatable, Flyable {
    void remoteControl();
}

Интерфейс наследует другие интерфейсы с помощью ключевого слова расширяет . Классы используют ключевые реализует унаследовать интерфейс.

5. Тип наследования

Когда класс наследует другой класс или интерфейсы, помимо наследования своих членов, он также наследует их тип. Это также относится к интерфейсу, который наследует другие интерфейсы.

Это очень мощная концепция, которая позволяет разработчикам программа к интерфейсу (базовый класс или интерфейс) , а не программирование для их реализации.

Например, представьте себе условие, при котором организация ведет список автомобилей, принадлежащих ее сотрудникам. Конечно, все сотрудники могут владеть различными моделями автомобилей. Итак, как мы можем относиться к различным экземплярам автомобилей? Вот решение:

public class Employee {
    private String name;
    private Car car;
    
    // standard constructor
}

Потому что все производные классы Автомобильные унаследовать тип Автомобильные , производные экземпляры классов могут быть переданы с помощью переменной класса Автомобильные :

Employee e1 = new Employee("Shreya", new ArmoredCar());
Employee e2 = new Employee("Paul", new SpaceCar());
Employee e3 = new Employee("Pavni", new BMW());

6. Скрытые члены класса

6.1. Скрытые члены инстанции

Что произойдет, если как суперкласс, так и подкласс определяют переменную или метод с одинаковым названием ? Не волнуйтесь; мы все еще можем получить доступ к обоим из них. Тем не менее, мы должны сделать наше намерение ясно Java, путем приставки переменной или метода с ключевыми этот или супер .

этот ключевое слово относится к экземпляру, в котором он используется. супер ключевое слово (как это кажется очевидным) относится к экземпляру родительского класса:

public class ArmoredCar extends Car {
    private String model;
    public String getAValue() {
    	return super.model;   // returns value of model defined in base class Car
    	// return this.model;   // will return value of model defined in ArmoredCar
    	// return model;   // will return value of model defined in ArmoredCar
    }
}

Многие разработчики используют этот и супер ключевые слова, чтобы четко узнать, какие переменные или метод они имеют в виду. Тем не менее, используя их со всеми членами может сделать наш код выглядеть суматоху.

6.2. Скрытые статические члены

Что происходит когда наш базовый класс и подклассы определяют статические переменные и методы с одинаковым названием ? Можем ли мы получить доступ к статические член из базового класса, в производной класс, как мы делаем для переменных экземпляра?

Рассмотрим пример:

public class Car {
    public static String msg() {
        return "Car";
    }
}
public class ArmoredCar extends Car {
    public static String msg() {
        return super.msg(); // this won't compile.
    }
}

Нет, мы не можем. Статические участники принадлежат к классу, а не к экземплярам. Таким образом, мы не можем использовать не статическое супер ключевое слово в msg() .

Поскольку статические участники принадлежат к классу, мы можем изменить предыдущий вызов следующим образом:

return Car.msg();

Рассмотрим следующий пример, в котором как базовый класс, так и производный класс определяют статический метод msg() с той же подписью:

public class Car {
    public static String msg() {
        return "Car";
    }
}
public class ArmoredCar extends Car {
    public static String msg() {
        return "ArmoredCar";
    }
}

Вот как мы можем их назвать:

Car first = new ArmoredCar();
ArmoredCar second = new ArmoredCar();

Для предыдущего кода во-первых.msg () выготовит ” и второй.msg () выготовит “ArmoredCar”. Называемое статическое сообщение зависит от типа переменной, используемой для обозначения ArmoredCar пример.

7. Заключение

В этой статье мы рассмотрели основной аспект языка Java – наследование.

Мы увидели, как Java поддерживает одно наследство с классами и несколькими наследования с интерфейсами и обсудили тонкости работы механизма на языке.

Как всегда, полный исходный код для примеров доступен более на GitHub .