Вагомі причини заборонити спадкування на Java?


178

Які є вагомі причини заборонити спадкування на Java, наприклад, використовуючи остаточні класи або класи, використовуючи єдиний приватний конструктор без параметрів? Які вагомі причини зробити метод остаточним?


1
Pedantry: конструктор за замовчуванням - це той, який генерується для вас компілятором Java, якщо ви явно не записуєте його у свій код. Ви маєте на увазі конструктор без аргументів.
Джон Топлі

Хіба це не "неявний конструктор за замовчуванням"?
кретцель

2
"Вам не потрібно надавати жодних конструкторів для свого класу, але ви повинні бути обережними при цьому. Компілятор автоматично надає конструктор без аргументів, за замовчуванням для будь-якого класу без конструкторів." java.sun.com/docs/books/tutorial/java/javaOO/constructors.html - Джон Топлі
Джон Топлі

Відповіді:


184

Ваша найкраща посилання тут - пункт 19 чудової книги Джошуа Блоха "Ефективна Java", який називається "Дизайн та документ для отримання спадщини або забороняйте його". (Це пункт 17 у другому виданні та пункт 15 у першому виданні.) Ви справді повинні прочитати його, але я підсумую.

Взаємодія успадкованих класів з їхніми батьками може бути дивовижною і непередбачуваною, якщо предка не було призначено для спадкування.

Тому класи повинні бути двох видів:

  1. Класи, розраховані на розширення , та з достатньою документацією, щоб описати, як це слід робити

  2. Заняття відзначені фінальними

Якщо ви пишете суто внутрішній код, це може бути непосильним. Однак додаткових зусиль, пов’язаних із додаванням п’яти символів до файлу класу, дуже мало. Якщо ви пишете лише для внутрішнього споживання, то майбутній кодер завжди може видалити «остаточний» - ви можете вважати це застереженням: «цей клас не був розроблений з урахуванням спадкування».


6
На мій досвід, це не зайве, якщо хтось, крім мене, буде використовувати код. Я ніколи не розумів, чому у Java за замовчуванням є методи, що перезаписуються.
Ерік Вайнау

9
Щоб зрозуміти деякі ефективні Java, вам потрібно зрозуміти, що Джош сприймає дизайн. Він каже [щось на зразок], ви завжди повинні створювати інтерфейси класів, як якщо б вони були публічним API. Я думаю, що думка більшості полягає в тому, що такий підхід зазвичай занадто важкий і негнучкий. (YAGNI та ін.)
Том Хоутін - тайклін

9
Хороша відповідь. (Я не можу втриматися, зазначивши, що це шість персонажів, оскільки тут також є пробіл ... ;-)
joel.neely

36
Зауважте, що проведення фінальних занять може ускладнити тестування (важче
заглушити

10
Якщо підсумковий клас пише в інтерфейс, глузування не повинно бути проблемою.
Фред Хаслам

26

Ви можете зробити метод остаточним, щоб переважаючі класи не могли змінити поведінку, на яку розраховано в інших методах. Методи, що називаються в конструкторах, часто оголошуються остаточними, тому при створенні об'єктів не виникає неприємних сюрпризів.


Не дуже зрозумів цю відповідь. Якщо ви не заперечуєте, чи можете ви пояснити, що ви маєте на увазі під тим, що "переважаючі класи не можуть змінити поведінку, на яку розраховують інші методи"? Прошу, по-перше, тому що я не зрозумів речення, а по-друге, тому що Netbeans рекомендує виконувати одну з моїх функцій, яку я дзвоню від конструктора final.
Nav

1
@Nav Якщо ви зробите метод остаточним, переважаючий клас не зможе мати власну версію цього методу (або це буде помилка компіляції). Таким чином ви знаєте, що поведінка, яку ви реалізуєте в цьому методі, не зміниться згодом, коли хтось інший розширить клас.
Білл Ящірка

19

Однією з причин зробити остаточний клас було б, якщо ви хочете примусити склад над спадщиною. Це, як правило, бажано, щоб уникнути щільної зв'язку між класами.


15

Є 3 випадки використання, коли можна перейти до остаточних методів.

  1. Щоб уникнути похідного класу від переопределення певної функціональності базового класу.
  2. Це з метою безпеки, коли базовий клас надає важливу функціональну основу рамки, коли похідний клас не повинен його змінювати.
  3. Остаточні методи швидші, ніж методи екземпляра, оскільки немає використання концепції віртуальної таблиці для кінцевих та приватних методів. Тож де колись є можливість, спробуйте скористатися заключними методами.

Призначення для завершення класу:

Так що жоден орган не може розширити ці заняття та змінити свою поведінку.

Напр .: Клас Wrapper Integer - це заключний клас. Якщо цей клас не є остаточним, то будь-який може поширити Integer на свій клас та змінити основну поведінку цілого класу. Щоб уникнути цього, java зробила всі класи обгортки як заключні класи.


Не дуже переконаний у своєму прикладі. Якщо це так, то чому б не зробити методи та змінні остаточними, а не зробити весь клас остаточним. Чи можете ви відредагувати свою відповідь деталями.
Раджкіран

4
Навіть якщо ви зробите всі змінні та методи остаточними, все ж інші можуть успадкувати ваш клас та додати додаткову функціональність та змінити основну поведінку вашого класу.
user1923551

3
> Якщо цей клас не є остаточним, тоді будь-який може поширити Integer на свій клас і змінити основну поведінку цілого класу. Скажімо, це було дозволено, і цей новий клас був названий DerivedInteger, він досі не змінює початковий клас Integer, і той, хто використовує DerivedIntegerце робить на свій страх і ризик, тому я досі не розумію, чому це проблема.
Сіддхартха


7

Спадщина - це як бензопила - дуже потужна, але жахлива в чужих руках. Або ви розробляєте клас, який має бути успадкований (що може обмежити гнучкість і зайняти набагато більше часу), або вам слід заборонити його.

Див. Ефективні пункти 16 та 17 другого видання Java або моє повідомлення в блозі "Податок на спадщину" .


Посилання на допис у блозі порушено. Крім того, ви вважаєте, що ви могли б трохи детальніше розглянути це?

@MichaelT: Виправлено посилання, дякую - дотримуйтесь його для більш детальної розробки :) (Боюся, я не маю часу до цього додати.)
Джон Скіт,

@JonSkeet, Посилання на допис у блозі порушено.
Люцифер

@Kedarnath: Це працює для мене ... це була зламана на деякий час, але це тепер вказує на правильну сторінку, на codeblog.jonskeet.uk ...
Джон Скит

4

Хммм ... я можу придумати дві речі:

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

Набагато реалістичнішим є те, щоб уникнути того, як об'єкт стає незмінним. Наприклад, оскільки струни незмінні, ваш код може безпечно зберігати посилання на неї

 String blah = someOtherString;

замість того, щоб скопіювати рядок спочатку. Однак, якщо ви можете підкласинг String, ви можете додати до нього методи, що дозволяють змінювати значення рядка, тепер жоден код не може покладатися на те, що рядок залишиться такою ж, якщо вона просто копіює рядок, як вище, замість цього вона повинна дублювати рядок.


2

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


2

Якщо ви позначаєте класи та методи як остаточні, ви можете помітити невеликий приріст продуктивності, оскільки під час виконання не потрібно шукати правильний метод класу, щоб викликати даний об’єкт. Незавершальні методи позначаються як віртуальні, щоб їх можна було належним чином розширити, а остаточні методи можна безпосередньо зв’язати або скласти вбудовано в класі.


1
Я вважаю, що це міф, оскільки вітрини сучасного покоління повинні мати можливість оптимізувати та деоптимізувати за потребою. Я пам'ятаю, як бачив це, і це класифікував як міф.
Мігель Пінг

1
Різниця в генерації коду не міф, але, можливо, підвищення продуктивності. Як я вже сказав, невеликий приріст, на одному з сайтів було зазначено 3,5-відсотковий коефіцієнт підвищення продуктивності, якийсь вище, що в більшості випадків не варто переходити всіх нацистів на ваш код.
seanalllogether

1
Це не міф. Фактично компілятор може вбудовувати лише V кінцеві методи. Я не впевнений, чи якісь JIT "вбудовують" речі в мистецтво під час виконання, але я сумніваюся, що це може, оскільки ви можете завантажувати похідний клас пізніше.
Урі

1
Microbenchmark == солі зерна. Однак після розминки циклу 10В середній показник понад 10В дзвінків практично не відрізняється від Eclipse на моєму ноутбуці. Два способи повернення String, остаточний - 6,9643us, нефінальний - 6,9641us. Ця дельта - фоновий шум, ІМХО.
joel.neely

1

Не дозволяти людям робити те, що може заплутати себе та інших. Уявіть бібліотеку фізики, де у вас є певні константи або обчислення. Не використовуючи остаточне ключове слово, хтось може зійти і переосмислити основні обчислення або константи, які НІКОЛИ не повинні змінюватись.


2
Щось я не розумію в цьому аргументі - якщо хтось змінив ті обчислення / константи, які не повинні змінюватися, - чи не виникли збої через те, що на 100% їхня вина? Іншими словами, як зміна користь нічого?
мат b

Так, це технічно «їх» вина, але уявіть, що хтось із бібліотеки, який не знав про зміни, може призвести до плутанини. Я завжди вважав, що це було причиною того, що багато класів Java Base було позначено остаточним, щоб люди не могли змінювати основні функціональні можливості.
Ансон Сміт

@matt b - Ви, здається, припускаєте, що розробник, який вніс зміни, зробив це так свідомо, або що вони будуть піклуватися про те, щоб це була їхня вина. Якщо хтось, окрім мене, буде використовувати мій код, я позначу його остаточним, якщо він не буде змінений.
Ерік Вайнау

Це насправді інше використання "остаточного" від того, про яке питали.
DJClayworth

Ця відповідь, схоже, плутає успадкування та змінність окремих полів із можливістю написання підкласу. Ви можете позначити окремі поля та методи як остаточні (запобігаючи їх переробленню), не забороняючи спадкування.
joel.neely

0

Ви хочете зробити метод остаточним, щоб переважаючі класи не змінювали його поведінку. Коли ви хочете мати можливість змінити поведінку, зробіть метод загальнодоступним. Коли ви перекриєте публічний метод, його можна змінити.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.