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
со всем необходимым интерфейсом.
Конструкция - это часть реализации, не интерфейс. Любой код, который успешно работает с интерфейсом, не заботится о конструкторе. Любой код, который заботится о конструкторе, в любом случае должен знать конкретный тип, а интерфейс можно игнорировать.
person
erickson
schedule
04.02.2009