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

вот пример:

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

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

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

IXMLizable.newInstanceFromXML(e);

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

EDIT: думаю, я ищу ответ это глубже, чем "потому что Java-это".

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

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

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

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

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

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

EDIT: начиная с Java 8, статические методы теперь разрешены в интерфейсах.

24 ответов


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

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

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

  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 которому не хватает "конструктора", и я создаю экземпляр и передаю его вашему коду, это is an IXMLizable со всем необходимым интерфейсом.

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


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

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

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

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

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

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

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

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


обычно это делается с помощью шаблона "Фабрика"

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

public interface IXMLizable {
  public Element toXMLElement();
}

С появлением Java 8 теперь можно писать по умолчанию и 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

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


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


Почему я не могу определить статический метод в 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));
    }    
}

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

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

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


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


  • "есть ли особая причина, по которой статические методы нельзя переопределить".

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

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

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


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

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

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}

комментировать EDIT: As of Java 8, static methods are now allowed in interfaces.

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


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

с дженериками они используют -- с реализацией по умолчанию или без нее. Очевидно, что нужно будет переопределить и так далее. Однако я предполагаю, что такое использование не было очень OO (как указывают другие ответы тупо) и, следовательно, не считалось стоящим усилий, которые они потребуют для полезной реализации.


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

например, я работаю с объектно-реляционным слоем, который имеет объекты value, но также имеет команды для управления объектами value. По разным причинам каждый класс объектов value должен определить некоторые статические методы, позволяющие платформе найти экземпляр команды. Например, создайте человека, которого вы бы сделали:

cmd = createCmd(Person.getCreateCmdId());
Person p = cmd.execute();

и загрузить человека по ID вы бы сделали

cmd = createCmd(Person.getGetCmdId());
cmd.set(ID, id);
Person p = cmd.execute();

Это довольно удобно, однако у него есть свои проблемы; в частности, существование статических методов не может быть применено в интерфейсе. Переопределяемый статический метод в интерфейсе был бы именно тем, что нам нужно, если бы он мог как-то работать.

EJBs решает эту проблему, имея домашний интерфейс; каждый объект знает, как найти свой дом, и дом содержит "статический метод. Таким образом," статические "методы могут быть переопределены по мере необходимости, и вы не загромождаете обычный (он называется" удаленный") интерфейс методами, которые не применяются к экземпляру вашего компонента. Просто сделайте так, чтобы обычный интерфейс указывал метод " getHome ()". Верните экземпляр домашнего объекта (который может быть одноэлементным, я полагаю), и вызывающий объект может выполнять операции, которые влияют на все объекты Person.


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

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


интерфейс не может быть разыменован статически, например,ISomething.member. Интерфейс всегда разыменован через переменную, которая ссылается на экземпляр подкласса интерфейс. Таким образом, ссылка на интерфейс никогда не может знать, на какой подкласс она ссылается без экземпляра своего подкласса.

таким образом, ближайшим приближением к статическому методу в интерфейсе будет нестатический метод, который игнорирует "это", т. е. не имеет доступа к каким-либо нестатическим членам экземпляра. В низкоуровневая абстракция, каждый нестатический метод (после поиска в любой vtable) - это просто функция с областью классов, которая принимает "это" как неявный формальный параметр. См.одноэлементный объект Scala и совместимость с Java как доказательство этой концепции. И поэтому каждый статический метод является функцией с областью действия класса, которая не принимает параметр "this". Таким образом, статический метод может быть вызван статически, но как и говорилось ранее, интерфейс не имеет реализации (это абстрактный.)

таким образом, чтобы получить самое близкое приближение к статическому методу в интерфейсе, нужно использовать нестатический метод, а затем не обращаться ни к одному из нестатических членов экземпляра. Не было бы никакого возможного преимущества производительности любым другим способом, потому что нет способа статически связать (во время компиляции) a ISomething.member(). Единственное преимущество статического метода в интерфейсе заключается в том, что он не вводит (т. е. игнорирует) неявное "это" и, таким образом, запрещает доступ к любому нестатическому экземпляру члены. Это неявно объявит, что функция, которая не имеет доступа к "this", является неизменной и даже не только для чтения по отношению к содержащему ее классу. Но объявление "статического" в интерфейсе ISomething также будет путать людей, которые пытались получить к нему доступ с ISomething.member() что вызовет ошибку компилятора. Я полагаю, что если ошибка компилятора была достаточно объяснительной, это было бы лучше, чем пытаться обучить людей использованию нестатического метода для достижения того, что они хотят (по-видимому в основном заводские методы), как мы делаем здесь (и был повторен для 3 Q&A раз на этом сайте), поэтому это, очевидно, проблема, которая не интуитивно понятна для многих людей. Я должен был подумать об этом некоторое время, чтобы получить правильное понимание.

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


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

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

надеюсь, что это поможет!


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

однако, если вы хотите, вы можете сделать это:

public class A {
  public static void methodX() {
  }
}

public class B extends A {
  public static void methodX() {
  }
}

в этом случае у вас есть два класса с 2 различными статическими методами, называемыми methodX().


Предположим, вы могли бы это сделать; рассмотрим этот пример:

interface Iface {
  public static void thisIsTheMethod();
}

class A implements Iface {

  public static void thisIsTheMethod(){
    system.out.print("I'm class A");
  }

}

class B extends Class A {

  public static void thisIsTheMethod(){
    System.out.print("I'm class B");
  } 
}

SomeClass {

  void doStuff(Iface face) {
    IFace.thisIsTheMethod();
    // now what would/could/should happen here.
  }

}

что может быть реализован статический интерфейс (вместо статического метода в интерфейсе). Все классы, реализующие данный статический интерфейс, должны реализовывать соответствующие статические методы. Вы можете получить статический интерфейс SI из любого класса clazz, используя

SI si = clazz.getStatic(SI.class); // null if clazz doesn't implement SI
// alternatively if the class is known at compile time
SI si = Someclass.static.SI; // either compiler errror or not null

затем вы можете позвонить si.method(params). Это было бы полезно (например, для шаблона проектирования фабрики), потому что вы можете получить (или проверить реализацию) реализацию статических методов SI из времени компиляции неизвестный класс ! Динамическая отправка необходима, и вы можете переопределить статические методы (если не final) класса, расширив его (при вызове через статический интерфейс). Очевидно, что эти методы могут обращаться только к статическим переменным своего класса.


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

у меня есть несколько определений перечисления, где я определил поля "id" и "displayName" вместе с вспомогательными методами, оценивающими значения по различным причинам. Реализация интерфейса позволяет мне гарантировать, что методы получения, но не статический вспомогательный метод. Будучи перечислением, на самом деле нет чистого способа выгрузить вспомогательные методы в унаследованный абстрактный класс или что-то подобное, поэтому методы должны быть определены в самом перечислении. Кроме того, поскольку это перечисление, вы никогда не сможете передать его как экземпляр объекта и рассматривать его как тип интерфейса, но возможность требовать существования статических вспомогательных методов через интерфейс-это то, что мне нравится в том, что он поддерживается в Java 8.

вот код, иллюстрирующий мою точку зрения.

интерфейс определение:

public interface IGenericEnum <T extends Enum<T>> {
    String getId();
    String getDisplayName();
    //If I was using Java 8 static helper methods would go here
}

пример определение enum:

public enum ExecutionModeType implements IGenericEnum<ExecutionModeType> {
    STANDARD ("Standard", "Standard Mode"),
    DEBUG ("Debug", "Debug Mode");

    String id;
    String displayName;

    //Getter methods
    public String getId() {
        return id;
    }

    public String getDisplayName() {
        return displayName;
    }

    //Constructor
    private ExecutionModeType(String id, String displayName) {
        this.id = id;
        this.displayName = displayName;
    }

    //Helper methods - not enforced by Interface
    public static boolean isValidId(String id) {
        return GenericEnumUtility.isValidId(ExecutionModeType.class, id);
    }

    public static String printIdOptions(String delimiter){
        return GenericEnumUtility.printIdOptions(ExecutionModeType.class, delimiter);
    }

    public static String[] getIdArray(){
        return GenericEnumUtility.getIdArray(ExecutionModeType.class);
    }

    public static ExecutionModeType getById(String id) throws NoSuchObjectException {
        return GenericEnumUtility.getById(ExecutionModeType.class, id);
    }
}

общее определение утилиты перечисления:

public class GenericEnumUtility {
    public static <T extends Enum<T> & IGenericEnum<T>> boolean isValidId(Class<T> enumType, String id) {       
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(enumOption.getId().equals(id)) {
                return true;
            }
        }

        return false;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String printIdOptions(Class<T> enumType, String delimiter){
        String ret = "";
        delimiter = delimiter == null ? " " : delimiter;

        int i = 0;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(i == 0) {
                ret = enumOption.getId();
            } else {
                ret += delimiter + enumOption.getId();
            }           
            i++;
        }

        return ret;
    }

    public static <T extends Enum<T> & IGenericEnum<T>> String[] getIdArray(Class<T> enumType){
        List<String> idValues = new ArrayList<String>();

        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            idValues.add(enumOption.getId());
        }

        return idValues.toArray(new String[idValues.size()]);
    }

    @SuppressWarnings("unchecked")
    public static <T extends Enum<T> & IGenericEnum<T>> T getById(Class<T> enumType, String id) throws NoSuchObjectException {
        id = id == null ? "" : id;
        for(IGenericEnum<T> enumOption : enumType.getEnumConstants()) {
            if(id.equals(enumOption.getId())) {
                return (T)enumOption;
            }
        }

        throw new NoSuchObjectException(String.format("ERROR: \"%s\" is not a valid ID. Valid IDs are: %s.", id, printIdOptions(enumType, " , ")));
    }
}

давайте допускается в интерфейсах: * Они заставят все реализующие классы объявить этот метод. * Интерфейсы обычно используются через объекты, поэтому единственными эффективными методами на них будут нестатические. * Любой класс, который знает определенный интерфейс, может вызывать свои статические методы. Следовательно, статический метод реализующего класса будет вызываться снизу,но класс invoker не знает, какой. Как узнать это? У него нет экземпляра для угадайте, что!

интерфейсы считались используемыми при работе с объектами. Таким образом, объект создается из определенного класса, поэтому этот последний вопрос решается. Вызывающий класс не должен знать, какой именно класс, потому что экземпляр может быть выполнен третьим классом. Таким образом, класс invoking знает только интерфейс.

Если мы хотим, чтобы это было распространено на статические методы, мы должны иметь возможность выделить класс реализации раньше, а затем передать ссылка на вызывающий класс. Это может использовать класс через статические методы в интерфейсе. Но в чем различие между этой ссылкой и объектом? Нам просто нужен объект, представляющий, что это был за класс. Теперь объект представляет старый класс и может реализовать новый интерфейс, включая старые статические методы - теперь они нестатические.

метаклассы служат для этой цели. Вы можете попробовать класс class Java. Но проблема в том, что Java не достаточно гибкий для этого. Вы не можете объявить метод в объекте класса интерфейса.

это мета-проблема-когда вам нужно сделать задницу

..бла бла

в любом случае у вас есть простой обходной путь - сделать метод нестатическим с той же логикой. Но тогда вам придется сначала создать объект для вызова метода.


чтобы решить эту проблему : ошибка: отсутствует тело метода, или объявить абстрактный static void main (String[] args);

interface I
{
    int x=20;
    void getValue();
    static void main(String[] args){};//Put curly braces 
}
class InterDemo implements I
{
    public void getValue()
    {
    System.out.println(x);
    }
    public static void main(String[] args)
    {
    InterDemo i=new InterDemo();
    i.getValue();   
    }

}

выход : 20

теперь мы можем использовать статический метод в интерфейс


Я думаю, что java не имеет статических методов интерфейса, потому что они вам не нужны. Вы можете думать, что знаете, но..... Как бы вы их использовали? Если вы хотите назвать их как

MyImplClass.myMethod()

тогда вам не нужно объявлять его в интерфейсе. Если вы хотите назвать их как

myInstance.myMethod()

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

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

MyImplClass.myMethod()

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


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