Каковы различия между абстрактными классами и интерфейсами в Java 8?
В Java раньше существовало тонкое, но важное различие между абстрактными классами и интерфейсами: реализации по умолчанию. Абстрактные классы могут иметь их, интерфейсы не могут. Java 8, тем не менее, представляет реализации по умолчанию для интерфейсов, то есть это больше не является критическим различием между интерфейсом и абстрактным классом.
Так что же?
Насколько я могу судить, единственное оставшееся отличие (за исключением, возможно, некоторых из них, связанных с эффективностью капота) состоит в том, что абстрактные классы следуют традиционному Java-наследованию, тогда как интерфейсы могут иметь множественное наследование (или множественную реализацию, если хотите). Это приводит меня к другому вопросу -
Как новые интерфейсы Java 8 позволяют избежать проблемы алмазов?
5 ответов
Интерфейсы не могут иметь связанное с ними состояние.
Абстрактные классы могут иметь состояние, связанное с ними.
Кроме того, методы по умолчанию в интерфейсах не должны быть реализованы. Таким образом, он не будет нарушать уже существующий код, так как интерфейс получает обновление, а реализующий класс не нуждается в его реализации.
В результате вы можете получить неоптимальный код, но если вы хотите иметь более оптимальный код, то ваша задача переопределить реализацию по умолчанию.
И, наконец, в случае возникновения проблемы с алмазом, компилятор предупредит вас, и вам нужно будет выбрать, какой интерфейс вы хотите реализовать.
Чтобы показать больше о проблеме алмаза, рассмотрите следующий код:
interface A {
void method();
}
interface B extends A {
@Override
default void method() {
System.out.println("B");
}
}
interface C extends A {
@Override
default void method() {
System.out.println("C");
}
}
interface D extends B, C {
}
Здесь я получаю ошибку компилятора interface D extends B, C
, тот:
interface D inherits unrelated defaults for method() form types B and C
Исправление:
interface D extends B, C {
@Override
default void method() {
B.super.method();
}
}
В случае, если я хотел унаследовать method()
от B
,
То же самое верно, если D
были class
,
Чтобы показать еще больше различий между интерфейсами и абстрактными классами в Java 8, рассмотрим следующее Team
:
interface Player {
}
interface Team {
void addPlayer(Player player);
}
Теоретически вы можете предоставить реализацию по умолчанию addPlayer
так что вы можете добавить игроков, например, в список игроков.
Но ждать...?
Как мне сохранить список игроков?
Ответ в том, что вы не можете сделать это в интерфейсе, даже если у вас есть доступные реализации по умолчанию.
Были некоторые очень подробные ответы, но в них, по-видимому, отсутствует один момент, который я, по крайней мере, считаю одним из очень немногих оправданий вообще иметь абстрактные классы:
Абстрактные классы могут иметь защищенные члены (и члены с видимостью по умолчанию). Методы в интерфейсах неявно общедоступны.
Определение алмазной проблемы является расплывчатым. Существуют всевозможные проблемы, которые могут возникнуть при множественном наследовании. К счастью, большинство из них легко обнаружить во время компиляции, и языки программирования поддерживают простые решения для решения этих проблем. Большинство из этих проблем даже не относятся к проблеме алмазов. Например, конфликтующие определения методов также могут встречаться без алмазов:
interface Bar {
default int test() { return 42; }
}
interface Baz {
default int test() { return 6 * 9; }
}
class Foo implements Bar, Baz { }
Конкретная проблема с бриллиантами - это вопрос включающего или исключающего. Если у вас есть иерархия типов, где B и C являются производными от A, а D - от B и C, тогда возникает вопрос:
- является D a B * и * a C (т.е. один тип A), или
- является D a B * или * a C (то есть два типа A).
Ну, в Java 8 тип А должен быть интерфейсом. Так что это не имеет значения, потому что интерфейсы не имеют состояния. Неважно, что интерфейсы могут определять методы по умолчанию, так как они также не имеют состояния. Они могут вызывать методы, которые имеют прямой доступ к состоянию. Однако эти методы всегда реализуются на основе единственного наследования.
Теперь, когда интерфейсы могут содержать исполняемый код, интерфейсы перенимают множество вариантов использования абстрактных классов. Но абстрактные классы все еще могут иметь переменные-члены, а интерфейсы - нет.
Алмазной проблемы можно избежать, просто не позволяя классу реализовать два интерфейса, когда оба интерфейса предоставляют реализацию по умолчанию для одного и того же метода с одинаковой сигнатурой.
Java 8, тем не менее, представляет реализации по умолчанию для интерфейсов, что означает, что это больше не является критическим различием между интерфейсом и абстрактным классом.
Тем не менее, есть еще несколько критических различий. Обратитесь к этому сообщению:
Интерфейс с методами по умолчанию против абстрактного класса в Java 8
Как новые интерфейсы Java 8 позволяют избежать проблемы алмазов?
Случай 1: вы реализуете два интерфейса, которые имеют одинаковые default
метод, вы должны разрешить конфликт в вашей реализации
interface interfaceA{
default public void foo(){
System.out.println("InterfaceA foo");
}
}
interface interfaceB{
default public void foo(){
System.out.println("InterfaceB foo");
}
}
public class DiamondExample implements interfaceA,interfaceB{
public void foo(){
interfaceA.super.foo();
}
public static void main(String args[]){
new DiamondExample().foo();
}
}
Выше пример производят ниже outout:
InterfaceA foo
Случай 2. Вы расширяете базовый класс и реализуете интерфейс с методом по умолчанию. Компилятор решает проблему с бриллиантом для вас, и вам не нужно решать ее, как в первом примере.
interface interfaceA{
default public void foo(){
System.out.println("InterfaceA foo");
}
}
class DiamondBase {
public void foo(){
System.out.println("Diamond base foo");
}
}
public class DiamondExample extends DiamondBase implements interfaceA{
public static void main(String args[]){
new DiamondExample().foo();
}
}
Приведенный выше пример:
Diamond base foo