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