Java 8 разрешает статические методы интерфейса
В Java 8 интерфейсы могут иметь статические методы. Они также могут иметь конкретные методы экземпляров, но не поля экземпляров.
Здесь действительно два вопроса:
- Почему в старые добрые времена интерфейсы не могли содержать статические методы?
- Почему статические методы не могут быть переопределены?
Статические методы в интерфейсах
Не было веской технической причины, по которой интерфейсы не могли иметь статические методы в предыдущих версиях. Это хорошо подытожено постером дублирующего вопроса. Методы статического интерфейса первоначально рассматривались как небольшое изменение языка, , а затем было официальное предложение добавить их в Java 7, но позже было отклонено из-за непредвиденных сложностей .
Наконец, Java 8 представила статические методы интерфейса, а также перезаписываемые методы экземпляров с реализацией по умолчанию. Они все еще не могут иметь поля экземпляра, хотя. Эти функции являются частью поддержки лямбда-выражений, и вы можете прочитать больше о них в Часть H JSR 335.
Переопределение статических методов
Ответ на второй вопрос немного сложнее.
Статические методы разрешимы во время компиляции. Динамическая диспетчеризация имеет смысл для методов экземпляра, где компилятор не может определить конкретный тип объекта и, следовательно, не может разрешить вызываемый метод. Но для вызова статического метода требуется класс, и поскольку этот класс известен статически - во время компиляции - динамическая диспетчеризация не требуется.
Небольшой фон о том, как работают методы экземпляра, необходим, чтобы понять, что здесь происходит. Я уверен, что фактическая реализация совсем другая, но позвольте мне объяснить мое представление о методе диспетчеризации, который модели точно наблюдал поведение.
Предположим, что у каждого класса есть хеш-таблица, которая отображает сигнатуры метода (имена и типы параметров) на фактический кусок кода для реализации метода. Когда виртуальная машина пытается вызвать метод в экземпляре, она запрашивает у объекта свой класс и ищет запрошенную подпись в таблице класса. Если тело метода найдено, оно вызывается. Иначе, родительский класс класса получен, и поиск там повторяется. Это продолжается до тех пор, пока метод не будет найден или родительских классов больше не будет - & mdash; в результате NoSuchMethodError
.
Если и суперкласс, и подкласс имеют записи в своих таблицах для одной и той же сигнатуры метода, сначала встречается версия подкласса, а версия суперкласса никогда не используется - это «переопределение».
Теперь предположим, что мы пропускаем экземпляр объекта и просто начинаем с подкласса. Разрешение может продолжаться, как описано выше, давая вам своего рода «переопределенный» статический метод. Однако все это может произойти во время компиляции, так как компилятор запускается из известного класса, а не ждет времени выполнения, чтобы запросить объект неопределенного типа для своего класса. Нет смысла «переопределять» статический метод, поскольку всегда можно указать класс, который содержит нужную версию.
Конструктор "Интерфейс"
Вот еще немного материала, посвященного недавнему редактированию вопроса.
Звучит так, будто вы хотите эффективно назначать метод, похожий на конструктор, для каждой реализации IXMLizable
. Забудьте о попытке применить это с помощью интерфейса на минуту и представьте, что у вас есть некоторые классы, отвечающие этому требованию. Как бы вы его использовали?
class Foo implements IXMLizable<Foo> {
public static Foo newInstanceFromXML(Element e) { ... }
}
Foo obj = Foo.newInstanceFromXML(e);
Поскольку вы должны явно назвать конкретный тип Foo
при «создании» нового объекта, компилятор может убедиться, что у него действительно есть необходимый фабричный метод. А если нет, ну и что? Если я смогу реализовать IXMLizable
, в котором отсутствует «конструктор», и я создам экземпляр и передам его вашему коду, он будет IXMLizable
со всем необходимым интерфейсом.
Конструкция является частью реализации, не интерфейс. Любой код, который успешно работает с интерфейсом, не заботится о конструкторе. Любой код, который заботится о конструкторе, должен в любом случае знать конкретный тип, и интерфейс можно игнорировать.