java - умолчанию - наследование интерфейсов




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

Вот пример:

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

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

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

IXMLizable.newInstanceFromXML(e);

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

EDIT: Я думаю, что я ищу ответ, который глубже, чем «потому что это так, как Java».

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

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

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

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

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

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

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


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

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

Согласно Java- docs.oracle/staticMethod :

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

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

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

list.sort(ordering);

вместо

Collections.sort(list, ordering);

Пример статического метода (из самого docs.oracle/staticMethod ):

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

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

Здесь есть два вопроса:

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

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

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

Наконец, в Java 8 были введены методы статического интерфейса, а также методы переопределения экземпляров с реализацией по умолчанию. Тем не менее, у них все еще нет полей экземпляров. Эти функции являются частью поддержки выражения лямбда, и вы можете больше узнать о них в части H JSR 335.

Переопределение статических методов

Ответ на второй вопрос немного сложнее.

Статические методы разрешимы во время компиляции. Динамическая диспетчеризация имеет смысл, например, методы, когда компилятор не может определить конкретный тип объекта и, таким образом, не может разрешить метод для вызова. Но для вызова статического метода требуется класс, и поскольку этот класс известен статически, то для компиляции время-динамическая отправка не нужна.

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

Представьте, что каждый класс имеет хеш-таблицу, которая отображает сигнатуры методов (имя и типы параметров) на фактический кусок кода для реализации метода. Когда виртуальная машина пытается вызвать метод в экземпляре, он запрашивает объект для своего класса и просматривает запрошенную подпись в таблице класса. Если тело метода найдено, оно вызывается. В противном случае получается родительский класс класса, и поиск повторяется там. Это продолжается до тех пор, пока метод не будет найден, или нет родительских классов, что приводит к NoSuchMethodError .

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

Теперь предположим, что мы пропустим экземпляр объекта и просто начнем с подкласса. Резолюция может выполняться, как указано выше, и дает вам своего рода «переопределяемый» статический метод. Однако разрешение может произойти во время компиляции, поскольку компилятор начинается с известного класса, а не ждет, пока среда выполнения не запросит объект неопределенного типа для своего класса. Нет смысла «переопределять» статический метод, так как всегда можно указать класс, содержащий нужную версию.

Конструктор "интерфейсы"

Вот немного больше материала, чтобы рассмотреть недавнее редактирование вопроса.

Похоже, вы хотите эффективно IXMLizable конструктор-подобный метод для каждой реализации IXMLizable . Забудьте о попытке навязать это с помощью интерфейса на минуту и ​​притворитесь, что у вас есть классы, соответствующие этому требованию. Как бы вы его использовали?

class Foo implements IXMLizable<Foo> {
  public static Foo newInstanceFromXML(Element e) { ... }
}

Foo obj = Foo.newInstanceFromXML(e);

Поскольку вы должны явно называть конкретный тип Foo при «конструировании» нового объекта, компилятор может убедиться, что он действительно имеет необходимый заводский метод. И если это не так, ну и что? Если я могу реализовать IXMLizable которому не хватает «конструктора», и я создаю экземпляр и IXMLizable его вашему коду, он является IXMLizable со всем необходимым интерфейсом.

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


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

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

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


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

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

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

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

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


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

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

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


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


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

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


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

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

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

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

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

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

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

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


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


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

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.
  }

}

С появлением 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

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


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

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

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

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

У меня есть несколько определений перечислений, где я определил поля «id» и «displayName» вместе со вспомогательными методами, оценивая значения по различным причинам. Внедрение интерфейса позволяет мне гарантировать, что методы геттера находятся на месте, но не статические вспомогательные методы. Являясь переименованием, действительно нет чистого способа разгрузить вспомогательные методы в унаследованный абстрактный класс или что-то подобное, поэтому методы должны быть определены в самом перечислении. Кроме того, поскольку это переименование, вы никогда не сможете передавать его как объект instanced и рассматривать его как тип интерфейса, но возможность требовать существования статических вспомогательных методов через интерфейс - это то, что мне нравится он поддерживается в 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
}

Пример одного определения перечисления:

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, " , ")));
    }
}

Чтобы решить эту проблему: ошибка: отсутствует тело метода или объявить абстрактный 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, чтобы помочь вам быть уверенным в этом.


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

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





static-methods