Вопрос-ответ

Why can't I define a static method in a Java interface?

Почему я не могу определить статический метод в интерфейсе Java?

РЕДАКТИРОВАТЬ: Начиная с Java 8, статические методы теперь разрешены в интерфейсах.

Вот пример:

public interface IXMLizable<T>
{
static T newInstanceFromXML(Element e);
Element toXMLElement();
}

Конечно, это не сработает. Но почему бы и нет?

Одна из возможных проблем заключается в том, что происходит при вызове:

IXMLizable.newInstanceFromXML(e);

В этом случае, я думаю, следует просто вызвать пустой метод (т.Е. {}). Все подклассы будут вынуждены реализовать статический метод, поэтому все они будут в порядке при вызове статического метода. Так почему же это невозможно?

РЕДАКТИРОВАТЬ: думаю, я ищу ответ более глубокий, чем "потому что так устроена Java".

Есть ли особая технологическая причина, по которой статические методы нельзя перезаписать? То есть, почему разработчики Java решили переопределить методы экземпляров, но не статические методы?

РЕДАКТИРОВАТЬ: Проблема с моим дизайном в том, что я пытаюсь использовать интерфейсы для обеспечения соблюдения соглашения о кодировании.

То есть цель интерфейса двоякая:


  1. Я хочу, чтобы интерфейс IXMLizable позволял мне преобразовывать классы, которые его реализуют, в XML-элементы (используя полиморфизм, работает нормально).


  2. Если кто-то захочет создать новый экземпляр класса, реализующий интерфейс IXMLizable , он всегда будет знать, что будет статический конструктор newInstanceFromXML(Элемент e).


Есть ли какой-либо другой способ обеспечить это, кроме простого добавления комментария в интерфейс?

Переведено автоматически
Ответ 1

Java 8 допускает статические методы интерфейса

В Java 8 интерфейсы могут иметь статические методы. У них также могут быть конкретные методы экземпляра, но не поля экземпляра.

На самом деле здесь возникает два вопроса:


  1. Почему в старые добрые времена интерфейсы не могли содержать статических методов?

  2. Почему статические методы нельзя переопределить?

Статические методы в интерфейсах

Не было серьезной технической причины, по которой интерфейсы не могли иметь статических методов в предыдущих версиях. Это прекрасно резюмируется в постере повторяющегося вопроса. Методы статического интерфейса изначально рассматривались как небольшое изменение языка, а затем появилось официальное предложение добавить их в 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 со всем необходимым интерфейсом.

Конструирование - это часть реализации, а не интерфейса. Любой код, который успешно работает с интерфейсом, не заботится о конструкторе. Любой код, который заботится о конструкторе, в любом случае должен знать конкретный тип, и интерфейс может быть проигнорирован.

Ответ 2

Этот вопрос уже задавался и на него были даны ответы, здесь

Продублировать мой ответ:

Никогда нет смысла объявлять статический метод в интерфейсе. Они не могут быть выполнены обычным вызовом MyInterface.staticMethod() . Если вы вызываете их, указывая реализующий класс MyImplementor.staticMethod(), то вы должны знать фактический класс, поэтому не имеет значения, содержит его интерфейс или нет.

Что еще более важно, статические методы никогда не переопределяются, и если вы попытаетесь сделать:

MyInterface var = new MyImplementingClass();
var.staticMethod();

правила для static говорят, что должен выполняться метод, определенный в объявленном типе var . Поскольку это интерфейс, это невозможно.

Причина, по которой вы не можете выполнить "result=MyInterface.staticMethod()", заключается в том, что для этого пришлось бы выполнить версию метода, определенную в MyInterface. Но в MyInterface не может быть определена версия, потому что это интерфейс. У него нет кода по определению.

Хотя вы можете сказать, что это равносильно "потому что Java делает это таким образом", на самом деле это решение является логическим следствием других проектных решений, также принятых по очень веским причинам.

Ответ 3

С появлением Java 8 теперь стало возможным записывать методы default и static в интерфейсе. документы.oracle/staticMethod

Например:

public interface Arithmetic {

public int add(int a, int b);

public static int multiply(int a, int b) {
return a * b;
}
}
public class ArithmaticImplementation implements Arithmetic {

@Override
public int add(int a, int b) {
return a + b;
}

public static void main(String[] args) {
int result = Arithmetic.multiply(2, 3);
System.out.println(result);
}
}

Результат : 6

СОВЕТ : Вызов метода статического интерфейса не требует реализации каким-либо классом. Конечно, это происходит потому, что для статических методов в интерфейсах применяются те же правила для статических методов в суперклассах.

Ответ 4

Обычно это делается с использованием заводского шаблона

public interface IXMLizableFactory<T extends IXMLizable> {
public T newInstanceFromXML(Element e);
}

public interface IXMLizable {
public Element toXMLElement();
}
2023-05-18 05:15 java