Чому абстрактний клас, що реалізує інтерфейс, може пропустити оголошення / реалізацію одного з методів інтерфейсу?


123

Цікава річ відбувається в Java, коли ви використовуєте абстрактний клас для реалізації інтерфейсу: деякі методи інтерфейсу можуть бути повністю відсутні (тобто немає абстрактної декларації чи реальної реалізації), але компілятор не скаржиться.

Наприклад, з урахуванням інтерфейсу:

public interface IAnything {
  void m1();
  void m2();
  void m3();
}

такий абстрактний клас весело складається без попередження або помилки:

public abstract class AbstractThing implements IAnything {
  public void m1() {}
  public void m3() {}
}

Чи можете ви пояснити, чому?


2
Не можна створити об'єкт абстрактного класу. Отже, поки реалізація не передбачена для абстрактного класу, об'єкти не можуть бути створені для IAnything. Так що це абсолютно добре для компілятора. Компілятор очікує, що будь-який не абстрактний клас, який реалізує IAnything, повинен реалізувати всі методи, оголошені з IAnything. А оскільки потрібно розширити та реалізувати AbstractThing, щоб мати можливість створювати об'єкти, компілятор видасть помилку, якщо ця реалізація не реалізує методи IAnything, що вийшов з AbstractThing.
ВанагаS

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

ви можете побачити відповідь на подібне запитання stackoverflow.com/questions/8026580/…
Do Nhu Vy

Відповіді:


156

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

Слідуючи прикладу коду, спробуйте скласти підклас AbstractThingбез застосування m2методу і подивитися, які помилки дає вам компілятор. Це змусить вас застосувати цей метод.


1
Я думаю, що компілятору слід все-таки кидати попередження щодо абстрактних класів, які реалізують інтерфейси неповно, просто тому, що тоді вам потрібно переглядати визначення 2 класу, а не 1, щоб побачити, що вам потрібно в підкласі. Це обмеження щодо мови / компілятора.
workmad3

3
Це не буде гарною ідеєю, оскільки, як правило, може бути безліч абстрактних класів, і «помилкові» попередження незабаром переграють вас, змусивши вас пропустити «справжні» попередження. Якщо ви подумаєте над цим, ключове слово "абстракт" має спеціально вказати компілятору подавити попередження для цього класу.
belugabob

4
@workmad - якщо у вас є спільна реалізація для підмножини методів інтерфейсу, то краще доцільно розподілити її до окремого базового класу (DRY
козирує одномісний

4
Було б небезпечно вимагати від вас розміщення порожніх реалізацій методу в абстрактному класі. Якщо ви це зробили, то реалізатори підкласів успадкували б це не поведінка, без того, щоб компілятор сказав їм, що існує проблема.
Білл Ящірка

8
Я думаю, що Workmad може запропонувати те, що ви визначаєте методи в абстрактному класі без тіла методу і позначаєте їх абстрактними. Мені це не здається поганою ідеєю.
Дональ

33

Ідеально чудово.
Ви не можете створити абстрактні класи. Але абстрактні класи можуть використовуватися для розміщення загальних реалізацій для m1 () та m3 ().
Таким чином, якщо реалізація m2 () відрізняється для кожної реалізації, але m1 і m3 не є. Ви можете створити різні конкретні реалізації IAnything з просто різною реалізацією м2 та походити від AbstractThing - вшановуючи принцип DRY. Перевірка, якщо інтерфейс повністю реалізований для абстрактного класу, марний.

Оновлення : Цікаво, що я вважаю, що C # виконує це як помилку компіляції. У цьому сценарії ви змушені копіювати підписи методів і префіксувати їх "абстрактним публічним" у абстрактному базовому класі .. (щось нове щодня :)


7

Добре. Щоб зрозуміти вищесказане, потрібно спочатку зрозуміти природу абстрактних класів. Вони схожі на інтерфейси в цьому відношенні. Про це тут говорять Oracle .

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

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

//Filename: Sports.java
public interface Sports
{
   public void setHomeTeam(String name);
   public void setVisitingTeam(String name);
}

//Filename: Football.java
public interface Football extends Sports
{
   public void homeTeamScored(int points);
   public void visitingTeamScored(int points);
   public void endOfQuarter(int quarter);
}

... як ви бачите, це також ідеально чудово. Просто тому, що, як і абстрактний клас, інтерфейс НЕ МОЖЕТ бути екземпляром. Отже, не потрібно чітко згадувати методи від свого «батьківського». Однак ВСІ батьківські підписи методу DO неявно стають частиною інтерфейсу, що розширюється, або реалізації абстрактного класу. Отже, як тільки власний клас (той, який можна створити інстанцію) розширює вище, потрібно буде забезпечити реалізацію кожного абстрактного методу.

Сподіваюсь, що це допоможе ... і Аллах, алам!


Це цікава точка зору. Змушує мене думати, що "абстрактні класи" - це справді "конкретні інтерфейси", тобто інтерфейси з деякими конкретними методами, а не класи з деякими абстрактними методами.
Джуліо Піанкастеллі

... трохи обох насправді. Але одне напевно, вони не миттєві.
Вдячний

4

Інтерфейс означає клас, який не реалізує свого методу, а лише з декларацією.
З іншого боку, абстрактний клас - це клас, який може мати реалізацію якогось методу разом з якимсь методом із просто декларацією, без реалізації.
Коли ми реалізуємо інтерфейс до абстрактного класу, це означає, що абстрактний клас успадкував усі методи інтерфейсу. Оскільки, не важливо впроваджувати весь метод у абстрактному класі, однак це стосується абстрактного класу (також у спадок), тому абстрактний клас може залишити частину методу в інтерфейсі без реалізації тут. Але, коли цей абстрактний клас буде успадкований деяким конкретним класом, їм доведеться реалізувати всі ці нереалізовані методи там у абстрактному класі.


4

Враховуючи інтерфейс:

public interface IAnything {
  int i;
  void m1();
  void m2();
  void m3();
}

Ось як насправді бачить Java:

public interface IAnything {
  public static final int i;
  public abstract void m1();
  public abstract void m2();
  public abstract void m3();
}

Таким чином, ви можете залишити деякі (або всі) з цих abstractметодів без виконання, так само, як у випадку з abstractкласами, що розширюють інший abstractклас.

Коли ви , правило , що всі методи повинні бути реалізовані в похідному , відноситься тільки до конкретної реалізації (тобто, що ні сам по собі).implementinterfaceinterfaceclassclassabstract

Якщо ви дійсно плануєте створити abstract classйого, тоді немає жодного правила, яке говорить про те, що ви дотримуєтесь implementвсіх interfaceметодів (зауважте, що в такому випадку обов'язково оголошувати похідне classяк abstract)


Використовується javap IAnything.classдля створення другого фрагмента коду.
шархп

3

Коли абстрактний клас реалізує інтерфейс

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

abstract class X implements Y {   
    // implements all but one method of Y
}

class XX extends X {   
    // implements the remaining method in Y 
} 

У цьому випадку клас X повинен бути абстрактним, оскільки він не повністю реалізує Y, але клас XX насправді реалізує Y.

Довідка: http://docs.oracle.com/javase/tutorial/java/IandI/ab абстракт.html


1

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

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