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

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

Вот пример:

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

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

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

IXMLizable.newInstanceFromXML(e);

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

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

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

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

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

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

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

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

Ответы [ 24 ]

490 голосов
/ 04 февраля 2009

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

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

Здесь действительно два вопроса:

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

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

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

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

46 голосов
/ 04 февраля 2009

Об этом уже спрашивали и отвечали, здесь

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

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

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

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

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

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

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

36 голосов
/ 14 февраля 2009

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

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

public interface IXMLizable {
  public Element toXMLElement();
}
33 голосов
/ 26 ноября 2014

С появлением Java 8 теперь можно писать стандартные и статические методы в интерфейсе. docs.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

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

19 голосов
/ 04 февраля 2009

Поскольку статические методы не могут быть переопределены в подклассах, и, следовательно, они не могут быть абстрактными. И все методы в интерфейсе: de facto , абстрактные.

10 голосов
/ 01 июля 2015

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

На самом деле вы можете в Java 8.

Согласно Java doc :

Статический метод - это метод, связанный с классом, в котором это определено, а не с любым объектом. Каждый экземпляр класса делится своими статическими методами

В Java 8 интерфейс может иметь методы по умолчанию и статические методы . Это облегчает нам организацию вспомогательных методов в наших библиотеках. Мы можем хранить статические методы, специфичные для интерфейса, в одном интерфейсе, а не в отдельном классе.

Пример метода по умолчанию:

list.sort(ordering);

вместо

Collections.sort(list, ordering);

Пример статического метода (из doc сам по себе):

public interface TimeClient {
    // ...
    static public ZoneId getZoneId (String zoneString) {
        try {
            return ZoneId.of(zoneString);
        } catch (DateTimeException e) {
            System.err.println("Invalid time zone: " + zoneString +
                "; using default time zone instead.");
            return ZoneId.systemDefault();
        }
    }

    default public ZonedDateTime getZonedDateTime(String zoneString) {
        return ZonedDateTime.of(getLocalDateTime(), getZoneId(zoneString));
    }    
}
6 голосов
/ 06 июля 2011

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

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

Скорее всего, правильный ответ заключается в том, что на момент определения языка не было ощущения необходимости в статических методах в интерфейсах. За прошедшие годы Java значительно выросла, и этот вопрос, по-видимому, вызвал некоторый интерес. То, что он рассматривался для Java 7, указывает на то, что он поднялся до уровня интереса, который может привести к изменению языка. Я, например, буду счастлив, когда мне больше не придется создавать экземпляр объекта просто для того, чтобы я мог вызвать свой нестатический метод получения, чтобы получить доступ к статической переменной в экземпляре подкласса ...

5 голосов
/ 05 февраля 2009
  • «Есть ли особая причина, по которой статические методы не могут быть переопределены».

Позвольте мне переформулировать этот вопрос для вас, заполнив определения.

  • «Есть ли особая причина, по которой методы, разрешенные во время компиляции, не могут быть разрешены во время выполнения.»

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

5 голосов
/ 04 февраля 2009

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

Но вы можете поместить классы, содержащие статические методы, в интерфейсы. Вы можете попробовать это!

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}
5 голосов
/ 04 февраля 2009

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

...