Статьи

Методы по умолчанию и множественное наследование

Недавно Lukas JOOQ Eder опубликовал и статью о вложенных классах и их использовании. Это интересная тема, и его статья, как всегда, интересна и заслуживает прочтения. Было только одно небольшое утверждение, с которым я не мог согласиться, и у нас была короткая цепочка ответов, приводящая к методу по умолчанию, и почему не может быть что-то вроде

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
class Outer {
    <non-static> interface Inner {
        default void x() {
            System.out.println(Outer.this.toString());
        }
    }
  
    Inner2 y() {
        return new Inner2();
    }
}
  
class Inner2 implements Inner {
}
  
// This would now print Outer.toString()
// to the console
new Outer().y().x();

на Яве. В приведенном выше коде метод по умолчанию для внутреннего интерфейса будет относиться к экземпляру, который включает в себя интерфейс, так сказать. Я считал, что «ответ» был не самой лучшей формой общения для этого, так как оригинальная тема была другой, и здесь я иду.

Какие методы по умолчанию

Вы, наверное, знаете. Если не Google, или прочитайте мои статьи Java 8 по умолчанию методы: что можно, а что нельзя? и Как не использовать методы Java 8 по умолчанию .

Если вы погуглили, вы увидите, что методы по умолчанию в Java 8 содержат Ханаан, доступно множественное наследование.

Существует очень хорошая дискуссия об этом над стековым потоком с настоящими профессионалами, которые знают Java:

У Java всегда было множественное наследование типов. Методы по умолчанию добавляют множественное наследование поведения, но не состояния. (Множественное наследование состояний в таких языках, как C ++, является источником большинства проблем.) — Брайан Гетц 21 июня ’14 в 2:05

В этой статье я немного расскажу, как интерпретировать и понять это утверждение.

Типы наследования

Цитата Брайана Гетца упоминает:

  • наследование типов
  • наследование поведения и
  • наследство гос.

Наследование типов очень просто и хорошо известно программистам Java. Вы определяете абстрактные методы в интерфейсе, но не указываете, как они работают, только возвращаемое значение и подпись методов. С методами по умолчанию Java 8 ввел наследование поведения без наследования состояния. Но разве можно наследовать поведение без наследования государства? На самом деле, нет. По крайней мере, в Java 8 вы можете иметь наследование состояния, хотя это не рекомендуется, неэффективно (я имею в виду: оно может быть медленным), а также громоздким и подверженным ошибкам программы. Но вы можете, и я покажу здесь, как. (В дополнение к ветке местной чепухи я опубликовал в статье, которую я упоминал выше.)

Я считаю, что изобретатели Java 8 хотели, чтобы метод по умолчанию сохранял обратную совместимость при реализации функциональных интерфейсов (например, потоков) в стандартное время выполнения. Недавно я смотрел сериал Fargo, и я чувствую, что дизайнеры языка просто забыли ответить «да» на вопрос «Ты действительно этого хочешь?»

Наследование состояний с помощью методов по умолчанию

Методы по умолчанию не могут получить доступ к полям (кроме статических полей, которые в любом случае являются окончательными в интерфейсах, поэтому давайте на время их забудем). Точно так же, как вы не можете получить доступ к закрытым полям класса A из класса B, расширяющего A. Или наоборот: вы не можете получить доступ к закрытым полям B из A. Однако вы можете иметь методы получения и установки в B, и если вы объявите их как абстрактные методы в A вы получаете доступ. Сезам, откройся. Геттеры и сеттеры являются решением.

Когда вы объявляете абстрактные методы в интерфейсе для всех полей состояния, к которым вы хотите получить доступ из методов по умолчанию, вы можете получить к ним доступ. Таким образом, вы получите тот же результат, как если бы существовало реальное наследование состояния. Разница заключается в синтаксисе: вы используете методы getter и setter вместо имени поля, и вы должны объявить их в интерфейсе. Таким образом, фаза компиляции проверяет, что геттеры и сеттеры действительно есть.

Вы можете видеть, что вещи с Java 8 становятся действительно сложными. Смешайте это с дженериками, и вы не сможете найти живую душу, которая все это понимает. Имея конструкцию, как

1
Outer.this.toString()

из приведенного выше примера кода, возможно, будет еще сложнее, без реальных рычагов.

Я считаю, что у меня есть некоторые знания о том, какие методы по умолчанию в Java 8 и что вы можете с ними сделать. Однако, имея 10 лет работы с Java и более 30 лет опыта программирования, я не могу сказать, как использовать методы по умолчанию. Я завидую разработчикам, которые все еще работают с Java 1.6 или более ранней версией в производственном коде: им не нужно беспокоиться о методах по умолчанию. (Это должно было быть шуткой.)

Хотя я пытаюсь дать несколько советов.

Рекомендация

Никогда не имитируйте наследование состояния в методах по умолчанию. Трудно сказать, что это на практике, хотя. Вызов геттера или сеттера явно есть. Вызов некоторых абстрактных методов, которые реализованы в реализующем классе, может быть или не быть. Если сомневаешься: лучше не надо.

Никогда не используйте трюк с локальным потоком, который я написал в другой статье.

Используйте методы по умолчанию для того, что использовали изобретатели языка Java: сохраняйте обратную совместимость в интерфейсах вашей библиотеки. Если вы когда-либо выпускали библиотеку, и она содержит интерфейс (как иначе, кстати) , не меняйте ее… Подумайте о клиентском коде, использующем вашу библиотеку, которая реализует интерфейс. С Java 8 у вас есть возможность закончить предложение: не меняйте его несовместимым. Если есть новый метод: создайте реализацию по умолчанию, чтобы код, который уже реализовал предыдущую версию, оставался совместимым, и нет необходимости расширять эти классы.

Ссылка: Методы по умолчанию и множественное наследование от нашего партнера JCG Питера Верхаса из блога Java Deep .