Перейти к содержимому

Как запретить наследование класса java

  • автор:

Как запретить наследование класса java

Наследование класса можно запретить, обозначив класс как final :

final class Parent  > class Child extends Parent  > 

Этот код выдаст ошибку cannot inherit from final Parent

Как запретить наследование класса java

CodeGalaxy

Каким образом можно запретить наследование класса (речь идет о top-level классах) ?

  • Добавить модификатор abstract
  • Добавить модификатор final
  • Добавить модификатор private
  • Не определять конструктор
  • Объявить все конструкторы класса private
  • Запретить наследование нельзя

Get an explanation when it’s available:

Войдите чтобы поставить Нравится

Войдите чтобы прокомментировать

Тесты по
Java

или Узнай больше о Тесты по Java онлайн

Следи за CodeGalaxy
Мобильное приложение Beta

Get it on Google Play

Тесты

Контент

Следите в соц-сетях

Правила

CodeGalaxy.io 2020.
Сделано с ❤️
����Киив ����Таллин ����Берлин, Штутгарт

Как запретить наследование класса java

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

class Person < String name; public String getName()< return name; >public Person(String name) < this.name=name; >public void display() < System.out.println("Name: " + name); >>

И, возможно, впоследствии мы захотим добавить еще один класс, который описывает сотрудника предприятия — класс Employee. Так как этот класс реализует тот же функционал, что и класс Person, поскольку сотрудник — это также и человек, то было бы рационально сделать класс Employee производным (наследником, подклассом) от класса Person, который, в свою очередь, называется базовым классом, родителем или суперклассом:

class Employee extends Person < public Employee(String name)< super(name); // если базовый класс определяет конструктор // то производный класс должен его вызвать >>

Чтобы объявить один класс наследником от другого, надо использовать после имени класса-наследника ключевое слово extends , после которого идет имя базового класса. Для класса Employee базовым является Person, и поэтому класс Employee наследует все те же поля и методы, которые есть в классе Person.

Если в базовом классе определены конструкторы, то в конструкторе производного классы необходимо вызвать один из конструкторов базового класса с помощью ключевого слова super . Например, класс Person имеет конструктор, который принимает один параметр. Поэтому в классе Employee в конструкторе нужно вызвать конструктор класса Person. То есть вызов super(name) будет представлять вызов конструктора класса Person.

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

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

public class Program < public static void main(String[] args) < Person tom = new Person("Tom"); tom.display(); Employee sam = new Employee("Sam"); sam.display(); >> class Person < String name; public String getName()< return name; >public Person(String name) < this.name=name; >public void display() < System.out.println("Name: " + name); >> class Employee extends Person < public Employee(String name)< super(name); // если базовый класс определяет конструктор // то производный класс должен его вызвать >>

Производный класс имеет доступ ко всем методам и полям базового класса (даже если базовый класс находится в другом пакете) кроме тех, которые определены с модификатором private . При этом производный класс также может добавлять свои поля и методы:

public class Program < public static void main(String[] args) < Employee sam = new Employee("Sam", "Microsoft"); sam.display(); // Sam sam.work(); // Sam works in Microsoft >> class Person < String name; public String getName()< return name; >public Person(String name) < this.name=name; >public void display() < System.out.println("Name: " + name); >> class Employee extends Person < String company; public Employee(String name, String company) < super(name); this.company=company; >public void work() < System.out.printf("%s works in %s \n", getName(), company); >>

В данном случае класс Employee добавляет поле company, которое хранит место работы сотрудника, а также метод work.

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

Производный класс может определять свои методы, а может переопределять методы, которые унаследованы от базового класса. Например, переопределим в классе Employee метод display:

public class Program < public static void main(String[] args) < Employee sam = new Employee("Sam", "Microsoft"); sam.display(); // Sam // Works in Microsoft >> class Person < String name; public String getName()< return name; >public Person(String name) < this.name=name; >public void display() < System.out.println("Name: " + name); >> class Employee extends Person < String company; public Employee(String name, String company) < super(name); this.company=company; >@Override public void display() < System.out.printf("Name: %s \n", getName()); System.out.printf("Works in %s \n", company); >>

Перед переопределяемым методом указывается аннотация @Override . Данная аннотация в принципе необязательна.

При переопределении метода он должен иметь уровень доступа не меньше, чем уровень доступа в базовом класса. Например, если в базовом классе метод имеет модификатор public, то и в производном классе метод должен иметь модификатор public.

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

class Employee extends Person < String company; public Employee(String name, String company) < super(name); this.company=company; >@Override public void display() < super.display(); System.out.printf("Works in %s \n", company); >>

С помощью ключевого слова super мы также можем обратиться к реализации методов базового класса.

Запрет наследования

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

public final class Person

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

class Employee extends Person

Кроме запрета наследования можно также запретить переопределение отдельных методов. Например, в примере выше переопределен метод display() , запретим его переопределение:

public class Person < //. public final void display()< System.out.println("Имя: " + name); >>

В этом случае класс Employee не сможет переопределить метод display.

Динамическая диспетчеризация методов

Наследование и возможность переопределения методов открывают нам большие возможности. Прежде всего мы можем передать переменной суперкласса ссылку на объект подкласса:

Person sam = new Employee("Sam", "Oracle");

Так как Employee наследуется от Person, то объект Employee является в то же время и объектом Person. Грубо говоря, любой работник предприятия одновременно является человеком.

Однако несмотря на то, что переменная представляет объект Person, виртуальная машина видит, что в реальности она указывает на объект Employee. Поэтому при вызове методов у этого объекта будет вызываться та версия метода, которая определена в классе Employee, а не в Person. Например:

public class Program < public static void main(String[] args) < Person tom = new Person("Tom"); tom.display(); Person sam = new Employee("Sam", "Oracle"); sam.display(); >> class Person < String name; public String getName() < return name; >public Person(String name) < this.name=name; >public void display() < System.out.printf("Person %s \n", name); >> class Employee extends Person < String company; public Employee(String name, String company) < super(name); this.company = company; >@Override public void display() < System.out.printf("Employee %s works in %s \n", super.getName(), company); >>

Консольный вывод данной программы:

Person Tom Employee Sam works in Oracle

При вызове переопределенного метода виртуальная машина динамически находит и вызывает именно ту версию метода, которая определена в подклассе. Данный процесс еще называется dynamic method lookup или динамический поиск метода или динамическая диспетчеризация методов.

Класс. Наследование. Запрет функции.

А как запретить, например, только функцию test(float i), а функцию test(BYTE i) оставить доступной?

22 ответа

05 октября 2009 года
1.2K / / 09.03.2007
Например так

class MyB : public MyA
private:
void test(float i)
MyA::test(i);
>
public:
void test(BYTE i)
MyA::test(i);
>
>;

06 октября 2009 года
29 / / 07.04.2008

Понятно, спасибо. Правда есть минус — если одинаковых ф-ий много, то переписывать придётся их все(

Ещё вопрос: Есть ли возможность запретить использование ф-ии (т.б. сказать компилятору, чтобы он в наследуемый класс не помещал такую-то ф-ию)? Мб директива есть для этого?

Примерно как-нибудь так:

class B : public A <
not_used A::test; // Что-то в таком роде (если такое возможно)
>; 06 октября 2009 года
4.8K / / 20.01.2000

Ещё вопрос: Есть ли возможность запретить использование ф-ии (т.б. сказать компилятору, чтобы он в наследуемый класс не помещал такую-то ф-ию)? Мб директива есть для этого?

А что значит «не помещал такую-то ф-ию в класс» ?

06 октября 2009 года
29 / / 07.04.2008
А что значит «не помещал такую-то ф-ию в класс» ?

В классе A ф-ия есть, а в классе B (наследуемого от класса А) небыло её (ф-ии) вообще.

06 октября 2009 года
1.2K / / 09.03.2007
В классе A ф-ия есть, а в классе B (наследуемого от класса А) небыло её (ф-ии) вообще.

А почему просто не ограничить к ней доступ посредством private?

06 октября 2009 года
2.7K / / 02.02.2008
Понятно, спасибо. Правда есть минус — если одинаковых ф-ий много, то переписывать придётся их все

Значит иерархия классов неправильная. Надо наследовать A и B от общего базового класса, в котором есть все необходимое обоим.

Можно сделать еще так:

struct A
void func1()<>
void func2()<>
void func3()<>
void func4()<>
>;

В принципе, при наследовании происходит примерно то же.

06 октября 2009 года
4.8K / / 20.01.2000
В классе A ф-ия есть, а в классе B (наследуемого от класса А) небыло её (ф-ии) вообще.

Реализация всегда наследуется полностью.
Можно ограничивать доступ. Можно реализовать примеси (стратегии), в т.ч. применив множественное наследование.

И все равно твой вопрос не понятен.
Что значит «не было вообще»?
Какая разница между «нет вообще» и «запрещена» (оба определения в нотации автора топика) ?
В каком контексте (для чего) задан вопрос?

08 октября 2009 года
1.1K / / 01.08.2005
Значит иерархия классов неправильная.

Тем не мение такие грабли встречаются. К примеру в Java есть возможность создать неизменяемую колекцыю данных с оригинальной. По сути создается оболочка над изначальной келекцыей в которой метод add не делегирует действие агрегату а генерирует есключение.

Кстати сопутствующий вопрос (С++ давно забыл) как при таком припрятывании метода розруливается приведение к родительскому типу ? Ибо во многих языках такой фокус невозможен и для скрытия метода приходится создавать новый интерфейс что тоже не всегда удобно.

08 октября 2009 года
1.2K / / 09.03.2007

Кстати сопутствующий вопрос (С++ давно забыл) как при таком припрятывании метода розруливается приведение к родительскому типу ? Ибо во многих языках такой фокус невозможен и для скрытия метода приходится создавать новый интерфейс что тоже не всегда удобно.

По большому счету никак. Если класс привести к базовому, метод все равно можно будет вызвать.

08 октября 2009 года
2.7K / / 02.02.2008

Rebbit, пока попытаюсь поэкспериментировать. Мои компиляторы (gcc, и тот, что в Экспресс Студии) не ругаются на такое:

int main()
MyA* aPtr = new MyB();
aPtr->test(20);
aPtr->test(20.1);
delete aPtr;
return 0;
>

если virtual убрать, то не будет методы MyB вызывать вообще. Вот и обманули запрет 🙂
08 октября 2009 года
1.2K / / 09.03.2007
Вот и обманули запрет 🙂

Помнится у Саттера в книжке такая глава была: как «нелегально» получить доступ к закрытым членам 🙂

08 октября 2009 года
2.7K / / 02.02.2008

Можно еще защищенное наследование использовать. Но это вообще будет близкий аналог агрегирования, которое я привел в первом ответе.

10 октября 2009 года
19 / / 14.09.2009

Kogrom
Ничего ты не обманул: virtual значит, что функции должны быть переопределены в наследнике. Когда ты убираешь слово «virtual» ты снимашь это требование.

10 октября 2009 года
4.8K / / 20.01.2000

Kogrom
Ничего ты не обманул: virtual значит, что функции должны быть переопределены в наследнике. Когда ты убираешь слово «virtual» ты снимашь это требование.

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

10 октября 2009 года
265 / / 16.12.2006
Kogrom
Когда ты убираешь слово «virtual» ты снимашь это требование.

Вы не правы. Функция, которая объявлена как виртуальная в базовом классе будет виртуальной в любом наследнике независимо от того, указано ли ключевое слово virtual в наследнике в объявлении функции.

Ключевое слово virtual в наследнике в этом случае используется ( или не используется:) ) только для повышения читабельности.

10 октября 2009 года
19 / / 14.09.2009

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

Я не говорил слова «должен». Я говорил слово «значит». Остальное твои фантазии.
Виртуальная функция может быть переопределена в наследнике, иначе она будет та же.

Вы не правы. Функция, которая объявлена как виртуальная в базовом классе будет виртуальной в любом наследнике независимо от того, указано ли ключевое слово virtual в наследнике в объявлении функции.

Ключевое слово virtual в наследнике в этом случае используется ( или не используется:) ) только для повышения читабельности.

Прочитай ещё раз и скажи где я говорил про ключевое слово virtual в наследнике ?

Не называй меня на «вы». Я буду обращптся к тебе как угодно.

10 октября 2009 года
1.2K / / 09.03.2007
Не называй меня на «вы». Я буду обращптся к тебе как угодно.

[QUOTE=Airhand;]
Kogrom
Ничего ты не обманул: virtual значит, что функции должны быть переопределены в наследнике. Когда ты убираешь слово «virtual» ты снимашь это требование.
[/QUOTE]

К чему этот ответ? Как уже заметил Green (а вы это проигнорировали, тупо придравшись к словам), virtual отнюдь не выставляет подобных требований.

[QUOTE=Airhand;]
Виртуальная функция может быть переопределена в наследнике, иначе она будет та же.
[/QUOTE]

Сами себе противоречите 😉

10 октября 2009 года
4.1K / / 05.01.2007

Kogrom
Ничего ты не обманул: virtual значит, что функции должны быть переопределены в наследнике. Когда ты убираешь слово «virtual» ты снимашь это требование.

мне кажется или чувак путает virtual с abstract?

10 октября 2009 года
4.8K / / 20.01.2000

Я не говорил слова «должен». Я говорил слово «значит». Остальное твои фантазии.
Виртуальная функция может быть переопределена в наследнике, иначе она будет та же.

Что-то с памятью или руки пишут не то что голова думает?
Перепрочти СВОЙ пост внимательно:

Ничего ты не обманул: virtual значит, что функции должны быть переопределены в наследнике. Когда ты убираешь слово «virtual» ты снимашь это требование.

10 октября 2009 года
19 / / 14.09.2009

Green
Да я сказал «должны». Я ошибся, на самом деле «может».

Почему, я же сказал, что буду обращаться к вам как вам будет угодно. А ко мне не надо обращаться на «вы».

К чему этот ответ? Как уже заметил Green (а вы это проигнорировали, тупо придравшись к словам), virtual отнюдь не выставляет подобных требований.

Вы хоть поняли, ЧТО написали ? Наследный класс это тот же базовый, которому добавили свойства и методы. Поэтому функция, определённая в базовом классе, будет и в наследном. Это легко проверить: выведете в функции что-нибудь на экран, а потом вызовите наследный класс. Только убедитесь, что функция в базовом классе вызывается в базовом классе.

10 октября 2009 года
2.7K / / 02.02.2008

Kogrom
Ничего ты не обманул: virtual значит, что функции должны быть переопределены в наследнике. Когда ты убираешь слово «virtual» ты снимашь это требование.

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

Я объясняю эту особенность так (вполне возможно, что вру): всякие слова для запрета (ограничения) доступа используются для того, чтобы компилятор не пропустил ошибочный код, чтобы не дал сослаться на закрытый метод вне класса. Но использовав указатель на родительский класс, мы «закрываем глаза» компилятору.

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

Где-то еще надо было важно сказать про срезку, но я не придумал где.

11 октября 2009 года
4.8K / / 20.01.2000

Вы хоть поняли, ЧТО написали ? Наследный класс это тот же базовый, которому добавили свойства и методы. Поэтому функция, определённая в базовом классе, будет и в наследном. Это легко проверить: выведете в функции что-нибудь на экран, а потом вызовите наследный класс. Только убедитесь, что функция в базовом классе вызывается в базовом классе.

Ты сам то понял, что написал?
Уже утомляет этот детский непрофессиональный лепет.
Прочитай хотя бы одну книгу по ООП, чтоб понять что такое наследование, какие виды наследования бывают, что именно наследуется при разных видах наследования.
У тебя проблемы как с пониманием предмета, так и с терминологией (в каждом предложении!).

P.S. Советую хорошенько задуматься над идеей почитать литературу прежде, чем опять начать нести ерунду или хамить.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *