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

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.
VanagaS
У меня был конкретный класс, который расширял свой собственный "AbstractThing" в сценарии, идентичном этому, и хотя я не реализовал один из методов в интерфейсе, он необъяснимо компилировался. Теперь он делает то, что я от него ожидаю, но я не могу понять, что раньше было причиной его успеха. Я подозреваю, что у меня не было ни :wодного файла.
Braden Best
вы можете увидеть ответ на аналогичный вопрос stackoverflow.com/questions/8026580/…
Do Nhu Vy

Ответы:

156

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

Следуя вашему примеру кода, попробуйте создать подкласс AbstractThingбез реализации m2метода и посмотрите, какие ошибки выдает компилятор. Это заставит вас реализовать этот метод.

Билл Ящерица
источник
1
Я думаю, что компилятор по-прежнему должен выдавать предупреждения об абстрактных классах, которые не полностью реализуют интерфейсы, просто потому, что тогда вам нужно просмотреть 2 определения класса, а не 1, чтобы увидеть, что вам нужно в подклассе. Однако это ограничение языка / компилятора.
workmad3
3
Это не было бы хорошей идеей, поскольку обычно может быть много абстрактных классов, и «ложные» предупреждения вскоре сокрушат вас, из-за чего вы пропустите «истинные» предупреждения. Если задуматься, ключевое слово abstract предназначено специально для того, чтобы указать компилятору подавлять предупреждения для этого класса.
belugabob
4
@workmad - если у вас есть общие реализации для подмножества методов интерфейса, имеет смысл выделить его в отдельный базовый класс (DRY превосходит код с одним местом)
Gishu
4
Было бы опасно требовать , чтобы вы помещали пустые реализации метода в абстрактный класс. Если бы вы это сделали, разработчики подклассов унаследовали бы это отсутствие поведения без того, чтобы компилятор сообщал им о проблеме.
Bill the Lizard
8
Я думаю, что рабочий может предложить вам определить методы в абстрактном классе без тела метода и пометить их как абстрактные. Мне это не кажется плохой идеей.
Dónal
33

Прекрасно.
Вы не можете создавать экземпляры абстрактных классов .. но абстрактные классы могут использоваться для размещения общих реализаций для m1 () и m3 ().
Итак, если реализация m2 () отличается для каждой реализации, а m1 и m3 - нет. Вы можете создавать разные конкретные реализации IAnything с помощью другой реализации m2 и унаследовать от AbstractThing - соблюдая принцип DRY. Проверять, полностью ли реализован интерфейс для абстрактного класса, бесполезно.

Обновление : что интересно, я обнаружил, что C # применяет это как ошибку компиляции. В этом сценарии вы вынуждены копировать сигнатуры методов и ставить перед ними префикс «abstract public» в абстрактном базовом классе .. (что-то новое каждый день :)

Gishu
источник
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);
}

... как видите, это тоже прекрасно компилируется. Просто потому, что, как и в случае с абстрактным классом, нельзя создать экземпляр интерфейса. Таким образом, не требуется явного упоминания методов своего «родителя». Однако ВСЕ сигнатуры родительских методов НЕявно становятся частью расширяемого интерфейса или реализации абстрактного класса. Итак, как только правильный класс (тот, который может быть создан) расширяет вышеуказанное, он БУДЕТ необходим для обеспечения реализации каждого отдельного абстрактного метода.

Надеюсь, что это поможет ... и Аллаху алам!

благодарный
источник
Это интересная точка зрения. Это заставляет меня думать, что «абстрактные классы» - это на самом деле «конкретные интерфейсы», т.е. интерфейсы с некоторыми конкретными методами, а не классы с некоторыми абстрактными методами.
Джулио Пьянкастелли
... немного того и другого. Но одно можно сказать наверняка: они не являются экземплярами.
Grateful
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класс.

Когда вы implementиспользуете interface, правило о том, что все interfaceметоды должны быть реализованы в производном class, применяется только к конкретной classреализации (т. Е. Которая abstractсама по себе не является).

Если вы действительно планируете создать abstract classиз него, то нет правила, которое говорит, что вы ко implementвсем interfaceметодам (обратите внимание, что в таком случае обязательно объявить производный classкак abstract)

sharhp
источник
Используется javap IAnything.classдля создания второго фрагмента кода.
Sharhp
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/abstract.html

До Нху Ви
источник
1

Абстрактные классы не требуются для реализации методов. Таким образом, даже если он реализует интерфейс, абстрактные методы интерфейса могут оставаться абстрактными. Если вы попытаетесь реализовать интерфейс в конкретном классе (т.е. не абстрактном) и не реализуете абстрактные методы, компилятор сообщит вам: либо реализуйте абстрактные методы, либо объявите класс как абстрактный.

Винсент Рамдхани
источник