Java не допускає багаторазове успадкування, але дозволяє реалізувати декілька інтерфейсів. Чому?
Java не допускає багаторазове успадкування, але дозволяє реалізувати декілька інтерфейсів. Чому?
Відповіді:
Тому що інтерфейси задають лише те, що робить клас, а не те, як він це робить.
Проблема багаторазового успадкування полягає в тому, що два класи можуть визначати різні способи здійснення однієї і тієї ж речі, і підклас не може вибрати, який вибрати.
Один із моїх викладачів коледжу пояснив мені це так:
Припустимо, у мене є один клас, який є Toaster, і інший клас, який є NuclearBomb. Вони можуть мати налаштування "темряви". Вони обидва мають метод (). (У одного вимкнено (), у іншого немає.) Якщо я хочу створити клас, який є підкласом обох цих ... як ви бачите, це проблема, яка справді може зірватися мені в обличчя .
Отож, одна з головних проблем полягає в тому, що якщо у вас є два батьківські класи, вони можуть мати різні реалізації однієї і тієї ж функції - або, можливо, дві різні функції з тим самим іменем, як у прикладі мого інструктора. Тоді вам доведеться вирішити, який саме ваш підклас буде використовувати. Звичайно, існують способи вирішення цього питання - C ++ так і робить, - але дизайнери Java вважають, що це ускладнить справи.
Однак, використовуючи інтерфейс, ви описуєте те, на що здатний робити клас, а не запозичуючи метод іншого класу. Кілька інтерфейсів набагато рідше можуть спричинити складні конфлікти, які потрібно вирішити, ніж кілька батьківських класів.
Оскільки спадкування надмірно використовується навіть тоді, коли ви не можете сказати "ей, цей метод виглядає корисним, я також продовжую цей клас".
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
Відповідь на це питання полягає у внутрішній роботі компілятора java (ланцюга конструктора). Якщо ми бачимо внутрішню роботу компілятора java:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank{
public void printBankBalance(){
System.out.println("20k");
}
}
Після складання це виглядає так:
public class Bank {
public Bank(){
super();
}
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank {
SBI(){
super();
}
public void printBankBalance(){
System.out.println("20k");
}
}
коли ми розширюємо клас і створюємо його об'єкт, один ланцюг конструктора буде працювати до Object
класу.
Наведений вище код буде працювати нормально. але якщо у нас є інший клас, Car
який називається, який розширюється, Bank
і один гібридний (множинний спадок) клас, який називається SBICar
:
class Car extends Bank {
Car() {
super();
}
public void run(){
System.out.println("99Km/h");
}
}
class SBICar extends Bank, Car {
SBICar() {
super(); //NOTE: compile time ambiguity.
}
public void run() {
System.out.println("99Km/h");
}
public void printBankBalance(){
System.out.println("20k");
}
}
У цьому випадку (SBICar) не вдасться створити ланцюг конструктора ( складати неоднозначність часу ).
Для інтерфейсів це дозволено, оскільки ми не можемо створити його об'єкт.
За новою концепцією default
та static
методом ласкаво зверніться до інтерфейсу за замовчуванням в інтерфейсі .
Сподіваємось, це вирішить ваш запит. Дякую.
Реалізація декількох інтерфейсів дуже корисна і не створює особливих проблем ні мовникам, ні програмістам. Так дозволено. Багатократне успадкування, хоча і корисне, може спричинити серйозні проблеми для користувачів (страшний діамант смерті ). І більшість речей, які ви робите з багаторазовим успадкуванням, можна зробити також складом або використанням внутрішніх класів. Тож багаторазове успадкування заборонено, оскільки приносить більше проблем, ніж прибуток.
ToyotaCar
і HybridCar
отримувались, Car
і переохоплювались Car.Drive
, і якщо PriusCar
успадковувались обоє, але не переосмислювалисьDrive
, система не мала б можливості визначити, що Car.Drive
має робити віртуальний . Інтерфейси уникають цієї проблеми, уникаючи наведеного вище курсивного стану.
void UseCar(Car &foo)
; не можна очікувати, що вони включатимуть розбіжність між ToyotaCar::Drive
та HybridCar::Drive
(оскільки воно часто не повинно ні знати, ні дбати про те, що ці інші типи навіть існують ). Мова може, як і C ++, вимагати, щоб цей код із ToyotaCar &myCar
бажанням передати його UseCar
потрібно спочатку передавати або на, HybridCar
або ToyotaCar
, але оскільки ((Автомобіль) (HybridCar) myCar) .Drive` і ((Car)(ToyotaCar)myCar).Drive
робив би різні речі, це означало б, що оновлення не зберігали ідентичність
Ви можете знайти точну відповідь на цей запит на сторінці документації oracle про багаторазове успадкування
Множинне успадкування стану: здатність успадковувати поля з декількох класів
Однією з причин, чому мова програмування Java не дозволяє вам поширювати більше ніж один клас, - це уникати питань багаторазового успадкування стану, що полягає у можливості успадкування полів з декількох класів
Якщо дозволено багатократне успадкування, і коли ви створюєте об'єкт шляхом інстанціювання цього класу, цей об'єкт успадкує поля з усіх суперкласів класу. Це спричинить два питання.
Множинне успадкування реалізації: Можливість успадкування визначень методу з декількох класів
Проблеми з таким підходом: конфлікт назви та неоднозначність . Якщо підклас та надклас містять однакове ім'я методу (та підпис), компілятор не може визначити, до якої версії слід звертатися.
Але java підтримує цей тип багаторазового успадкування методами за замовчуванням , які були запроваджені з моменту випуску Java 8. Компілятор Java надає деякі правила для визначення методу за замовчуванням для конкретного класу.
Детальнішу інформацію щодо вирішення проблеми з алмазами див. У розділі SE внизу:
Які відмінності між абстрактними класами та інтерфейсами в Java 8?
Багатократне успадкування типу: Можливість класу реалізувати більше одного інтерфейсу.
Оскільки інтерфейс не містить змінних полів, вам не доведеться турбуватися про проблеми, що виникають внаслідок багаторазового успадкування стану тут.
Кажуть, що стан об'єктів посилається на поля в ньому, і це стане неоднозначним, якби надто багато класів успадковувалося. Ось посилання
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
Java підтримує багатократне успадкування лише через інтерфейси. Клас може реалізовувати будь-яку кількість інтерфейсів, але може розширювати лише один клас.
Багаторазове успадкування не підтримується, оскільки це призводить до смертельної проблеми з алмазами. Однак це може бути вирішено, але це призводить до складної системи, тому засновники Java відмовилися від багаторазового успадкування.
У Білій книзі під назвою «Java: огляд» Джеймса Гослінга в лютому 1995 року ( посилання ) дається уявлення про те, чому багатоточне успадкування не підтримується на Java.
За словами Гослінга:
"JAVA опускає багато рідко використовуваних, погано зрозумілих, заплутаних особливостей C ++, які, за нашим досвідом, приносять більше горя, ніж користі. Це, насамперед, перевантаження оператора (хоча у нього є перевантаження методом), багаторазового успадкування та широких автоматичних примусів".
З тієї ж причини C # не дозволяє множинне успадкування, але дозволяє реалізувати декілька інтерфейсів.
Урок, отриманий з C ++ w / багаторазового успадкування, полягав у тому, що це призвело до більшої кількості питань, ніж варто.
Інтерфейс - це договір речей, які ваш клас повинен реалізувати. Ви не отримуєте жодної функціональності від інтерфейсу. Спадщина дозволяє успадкувати функціональність батьківського класу (і при множинному успадкуванні, що може отримати надзвичайно заплутаність).
Дозвіл декількох інтерфейсів дозволяє використовувати шаблони дизайну (наприклад, адаптер) для вирішення тих же типів проблем, які ви можете вирішити, використовуючи множинне спадкування, але набагато більш надійним і передбачуваним способом.
D1
і D2
обидва успадковують з B
, і кожен переосмислює функцію f
, і якщо obj
це екземпляр типу, S
який успадковує і те, D1
і інше, D2
але не переосмислює f
, тоді при передачі посилання на S
це D1
слід отримувати те, що f
використовує D1
переопределення, а кастинг не B
повинен змінити це. Аналогічно, при передаванні посилання S
на нього D2
слід отримувати те, що f
використовує D2
переопределення, а лиття не B
повинно цього змінювати. Якщо мові не потрібно було дозволити додавання віртуальних членів ...
Оскільки ця тема не є близькою, я опублікую цю відповідь, я сподіваюся, що це допоможе комусь зрозуміти, чому java не дозволяє багаторазово успадкувати.
Розглянемо наступний клас:
public class Abc{
public void doSomething(){
}
}
У цьому випадку клас Abc нічого не поширює? Не так швидко цей клас неявно розширює клас Object, базовий клас, який дозволяє всім працювати на Java. Все є об’єктом.
Якщо ви намагаєтеся використовувати клас вище , ви побачите , що ваш IDE дозволяє використовувати такі методи , як: equals(Object o)
, toString()
і т.д., але ви не оголосите ці методи, вони прийшли з базового класуObject
Ви можете спробувати:
public class Abc extends String{
public void doSomething(){
}
}
Це добре, тому що ваш клас не буде неявно розширюватись, Object
а розширюватиметься, String
оскільки ви це сказали. Розглянемо таку зміну:
public class Abc{
public void doSomething(){
}
@Override
public String toString(){
return "hello";
}
}
Тепер ваш клас завжди повернеться "привіт", якщо ви зателефонуєте доString ().
Тепер уявіть собі наступний клас:
public class Flyer{
public void makeFly(){
}
}
public class Bird extends Abc, Flyer{
public void doAnotherThing(){
}
}
Знову клас Flyer
неявно розширює Об'єкт, у якого є метод toString()
, у будь-якого класу буде цей метод, оскільки всі вони поширюються Object
опосередковано, тому, якщо ви телефонуєте toString()
зBird
, який toString()
Java повинен був би використовувати? З Abc
або Flyer
? Це станеться з будь-яким класом, який намагається розширити два або більше класів, щоб уникнути подібного «зіткнення методу», в якому вони побудували ідею інтерфейсу , в основному ви могли б вважати їх абстрактним класом, який не розширює об’єкт опосередковано . Оскільки вони абстрактні, їх доведеться реалізувати класом, який є об'єктом (інтерфейс не можна створити самостійно, він повинен бути реалізований класом), тому все буде працювати нормально.
Щоб відрізняти класи від інтерфейсів, ключові слова реалізовано лише для інтерфейсів.
Ви можете реалізувати будь-який інтерфейс, який вам подобається, в тому ж класі, оскільки він за замовчуванням нічого не розширює (але ви можете створити інтерфейс, який розширює інший інтерфейс, але знову ж таки, "батьковий" інтерфейс не поширює "Об'єкт"), тому інтерфейс є просто інтерфейс, і вони не страждатимуть від " методів зіткнення підписів ", якщо вони виконають компілятор, викличуть вам попередження, і вам доведеться просто змінити підпис методу, щоб виправити це (підпис = ім'я методу + парами + тип повернення) .
public interface Flyer{
public void makeFly(); // <- method without implementation
}
public class Bird extends Abc implements Flyer{
public void doAnotherThing(){
}
@Override
public void makeFly(){ // <- implementation of Flyer interface
}
// Flyer does not have toString() method or any method from class Object,
// no method signature collision will happen here
}
Тому що інтерфейс - це лише договір. І клас насправді є контейнером для даних.
Наприклад, два класу A, B, що мають однаковий метод m1 (). А клас C поширює і A, B.
class C extends A, B // for explaining purpose.
Тепер клас C буде шукати визначення m1. По-перше, він буде шукати в класі, якщо він не знайшов, то він перевірить для батьків батьків. Обидва A, B мають визначення. Отже, тут виникає неоднозначність, яке визначення слід вибрати. Тож JAVA НЕ ПІДТРИМУЄТЬСЯ МНОГО СВІТЛЕННЯ.
Java не підтримує багаторазове успадкування з двох причин:
Object
класу. Коли він успадковується від більш ніж одного суперкласу, підклас отримує неоднозначність для придбання властивості класу Object ..super()
конструктора класу supper. Якщо в класі є більше одного суперкласу, він плутається.Отже, коли один клас поширюється на більш ніж один суперклас, ми отримуємо помилку часу компіляції.
Візьмемо для прикладу випадок, коли клас A має метод getSomething, а клас B метод getSomething, а клас C розширює A і B. Що буде, якщо хтось назвав C.getSomething? Немає способу визначити, який метод викликати.
В основному інтерфейси просто вказують, які методи повинен містити клас реалізації. Клас, який реалізує кілька інтерфейсів, означає, що клас повинен реалізувати методи з усіх цих інтерфейсів. Whci не призведе до жодних проблем, як описано вище.
Розглянемо сценарій, коли Test1, Test2 і Test3 - це три класи. Клас Test3 успадковує класи Test2 та Test1. Якщо в класах Test1 та Test2 є один і той же метод, і ви називаєте його з дочірнього об'єкта класу, буде неоднозначність виклику методу випробувань класу Test1 або Test2, але немає такої неоднозначності для інтерфейсу, як в інтерфейсі ніякої реалізації немає.
Java не підтримує багатократне успадкування, багатошаровість та гібридне успадкування через неоднозначність:
Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ? So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.
Посилання: https://plus.google.com/u/0/communities/102217496457095083679
простий спосіб, який ми всі знаємо, ми можемо успадкувати (розширити) один клас, але ми можемо реалізувати так багато інтерфейсів .. це тому, що в інтерфейсах ми не даємо реалізації просто говорити про функціональність. Припускаю , якщо Java може , поширюється так багато класів і ті ж методи , є .. в цей момент , якщо ми спробуємо викликати метод суперкласу в класі півдня , який метод передбачає , щоб запустити ??, компілятору заплутатися приклад - спроба кратному триває , але в інтерфейси цих методів не мають тіл, ми повинні реалізовувати їх у підкласі .. спробуйте декілька реалізацій, щоб не хвилюватися ..
* Це проста відповідь, оскільки я початківець у Java *
Вважайте, що є три класи X
, Y
і Z
.
Тож ми успадковуємо як X extends Y, Z
І, так Y
і обидва, і Z
маємо метод alphabet()
з тим же типом повернення та аргументами. Цей метод alphabet()
в Y
повідомляє, щоб відобразити перший алфавіт, а алфавіт методу в Z
зазначеному відобразити останній алфавіт . Тож тут виникає неоднозначність, коли alphabet()
його викликають X
. Чи означає це, щоб відображати перший чи останній алфавіт ??? Тож java не підтримує багаторазове успадкування. У випадку інтерфейсів розглянемо Y
і Z
як інтерфейси. Таким чином, обидва будуть містити декларацію методуalphabet()
але не визначення. Він не вказуватиме, чи потрібно відображати перший алфавіт чи останній алфавіт чи що-небудь, але просто оголосить методalphabet()
. Тож немає підстав піднімати неоднозначність. Ми можемо визначити метод за допомогою чого завгодно всередині класу X
.
Отже, одним словом, визначення інтерфейсів робиться після реалізації, так що не плутанина.