Java 8 дозволяє статичні методи інтерфейсу
У Java 8 інтерфейси можуть мати статичні методи. Вони також можуть мати конкретні примірники методів, але не поля екземплярів.
Тут справді два питання:
- Чому в погані старі часи інтерфейси не могли містити статичні методи?
- Чому статичні методи не можна перекрити?
Статичні методи в інтерфейсах
Не було жодної сильної технічної причини, чому інтерфейси не могли мати статичні методи в попередніх версіях. Це красиво підсумовано плакатом дублюючого питання. Статичні методи інтерфейсу спочатку розглядалися як невелика зміна мови, а потім була офіційна пропозиція додати їх у Java 7, але пізніше її відмовили через непередбачені ускладнення.
Нарешті, Java 8 представила методи статичного інтерфейсу, а також способи переопределення екземплярів із реалізацією за замовчуванням. Вони все ще не можуть мати поля примірників. Ці функції є частиною підтримки лямбда-експресії, і ви можете прочитати більше про них у частині H JSR 335.
Переважаючі статичні методи
Відповідь на друге питання дещо складніша.
Статичні методи вирішуються під час компіляції. Динамічна диспетчеризація має сенс для, наприклад, методів, коли компілятор не може визначити конкретний тип об'єкта, і, таким чином, не може вирішити метод для виклику. Але для виклику статичного методу потрібен клас, а оскільки цей клас статично відомий - у час компіляції - динамічна відправка непотрібна.
Невелика інформація про те, як працюють методи екземплярів, необхідна, щоб зрозуміти, що тут відбувається. Я впевнений, що реальна реалізація зовсім інша, але дозвольте мені пояснити моє поняття відправлення методу, в яких моделях точно спостерігалася поведінка.
Притворіть, що для кожного класу є хеш-таблиця, яка відображає підписи методів (типи імен та параметрів) у фактичний фрагмент коду для реалізації методу. Коли віртуальна машина намагається викликати метод на екземпляр, він запитує об'єкт для свого класу та шукає запитуваний підпис у таблиці класу. Якщо знайдено тіло методу, воно викликається. В іншому випадку отримується батьківський клас класу, і пошук там повторюється. Це триває до тих пір, поки метод не буде знайдений або не існує більше батьківських класів, що призводить до появи a NoSuchMethodError
.
Якщо обидва надкласи та підкласи мають записи у своїх таблицях для одного і того ж підпису методу, спочатку зустрічається версія підкласу, а версія суперкласу ніколи не використовується - це "перевизначення".
Тепер, припустимо, ми пропускаємо екземпляр об'єкта і просто починаємо з підкласу. Роздільна здатність може продовжуватися так, як зазначено вище, надаючи свого роду "перезаписний" статичний метод. Роздільна здатність може статися в час компіляції, оскільки компілятор починає з відомого класу, а не чекає, поки час виконання буде запитувати об'єкт не визначеного типу для його класу. Немає сенсу "переосмислювати" статичний метод, оскільки завжди можна вказати клас, який містить бажану версію.
"Інтерфейси" конструктора
Ось трохи більше матеріалу для вирішення цього питання.
Це здається, що ви хочете ефективно призначити конструктор-подібний метод для кожної реалізації IXMLizable
. Забудьте про те, щоб спробувати застосувати це за допомогою інтерфейсу на хвилину, і зробіть вигляд, що у вас є кілька класів, які відповідають цій вимозі. Як би ти ним користувався?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
Оскільки вам потрібно чітко назвати конкретний тип Foo
при "конструюванні" нового об'єкта, компілятор може перевірити, чи справді він має необхідний заводський метод. А якщо це не так, то що? Якщо я можу реалізувати , IXMLizable
що не вистачає «конструктора», і я створюю примірник і передати його в код, він єIXMLizable
всім необхідним інтерфейсом.
Побудова є частиною реалізації, а не інтерфейсом. Будь-який код, який успішно працює з інтерфейсом, не хвилює конструктор. Будь-який код, який піклується про конструктор, все одно повинен знати конкретний тип, і інтерфейс можна ігнорувати.