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

499

РЕДАКТИРОВАТЬ: Начиная с 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).

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

cdmckay
источник
4
Вам не нужно загромождать определения методов (и полей) публичными в интерфейсах, кстати.
Том Хотин - tackline
Хм, похоже, дубликат stackoverflow.com/questions/21817/… . Не видел этого раньше.
Майкл Майерс
1
Не могли бы вы предоставить код, как бы вы хотели использовать статические методы интерфейса?
Павел Фельдман
43
Это будет возможно в Java 8: docs.oracle.com/javase/tutorial/java/IandI/…
dakshang
1
@dakshang Да, но он не делает то, что хочет ОП.
user253751

Ответы:

518

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 всем необходимым интерфейсом.

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

Эриксон
источник
Хороший звонок на Project Coin. mail.openjdk.java.net/pipermail/coin-dev/2009-March/000117.html
Майкл Майерс
12
Может ли причина № 1 быть множественным наследованием? Поскольку мы можем наследовать от нескольких интерфейсов, если два интерфейса содержали одну и ту же сигнатуру статического метода, а затем класс реализовал их оба и вызвал этот метод, то все может усложниться таким образом, которого создатели языка Java хотели бы избежать, запретив наследование нескольких классов в первое место. Тот же аргумент, очевидно, может быть сделан для интерфейса, не допускающего никакого определения метода в них вообще.
shrini1000
1
@ shrini1000 - Нет, статические методы разрешаются во время компиляции. Неоднозначность может быть обработана так же, как с константами: с ошибкой компилятора. Однако предложение в рамках Project Coin было отклонено, сославшись на некоторые непредвиденные трудности. Не уверен, что они были, но я не думаю, что это было в том же духе.
Эриксон
1
@ tgm1024 Да, раздел «Интерфейсы конструктора» объясняет, почему не имеет смысла пытаться вызывать полиморфное поведение через тип, известный во время компиляции. Как бы вы вызвали RESET()на данный класс? Ты бы написал SomeClass.RESET(). Таким образом, вам не нужен интерфейс для описания этого API; это статично. Интерфейсы используются, когда вы не знаете конкретный тип во время компиляции. Это никогда не происходит со статическим методом.
Эриксон
2
«Конструкция является частью реализации, а не интерфейса. Любой код, который успешно работает с интерфейсом, не заботится о конструкторе». - Это явно не правда. На других языках (например, Swift) я могу создавать новые экземпляры, Tне зная Tстатически, потому что я обещаю в интерфейсе, что определенный конструктор (или статический метод) будет существовать во время выполнения. Тот факт, что поколение невозможно определить в Java, не означает, что это не имеет смысла делать.
Рафаэль
48

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

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

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

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

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

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

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

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

DJClayworth
источник
14
Если вы используете <T extends MyInterface> в качестве параметра универсального типа, было бы неплохо через интерфейс гарантировать, что T может .doSomething ().
Крис Бетти
4
Хотя я понимаю аргументы, я согласен с @Chris_Betti (даже для неуниверсальных типов): было бы хорошо, чтобы структура кода гарантировала, что некоторые классы реализуют определенный статический API. Может быть, это возможно, используя другую концепцию ...
Juh_
@Juh_, ..... или новое ключевое слово, если это абсолютно необходимо. В staticлюбом случае, я считаю, что это терминология в языках, и она слишком растянута, как есть. Так что само по себе это уже отрывочно. Смотрите мой пример выше stackoverflow.com/questions/512877/… {shrug}.
3
Это кажется неправдой: «Нет смысла объявлять статический метод в интерфейсе». Если у меня есть коллекция классов, которые, без создания экземпляра, могли бы предложить мне некоторую информацию, но мне понадобился бы общий интерфейс для размещения этой статической информации уровня класса (т. Е. Интерфейс с переопределяемым статическим методом), тогда это правильное использование , Подумайте об отражении ++, где вы могли бы собирать метаинформацию о свойствах класса без использования атрибутов, отражения и т. Д.
Jason
1
«Нет смысла объявлять статический метод в интерфейсе». Это не так: представьте, что в вашей системе установлен распознаватель классов по умолчанию. Если он обнаружит, что вы реализуете метод ContainerInjectionInterce :: create (Container $ container), то он, например, создаст объект с этой функцией.
user3790897
37

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

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

public interface IXMLizable {
  public Element toXMLElement();
}
Питер Лори
источник
7
+1 заводской шаблон звучит как решение проблемы. (хотя не к вопросу)
pvgoddijn
Может кто-нибудь сказать мне, что означает помещать <T extends IXMLizable> здесь. Я новичок в Java. Что это делает?
Нуван Харшакумара Пияратна
1
@NuwanHarshakumaraPiyarathna T должен быть классом, расширяющим IXMLizable. Посмотрите на дженерики Java для лучшего понимания того, что это значит
Адриан
37

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

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

жулик
источник
Это прекрасный пример этого вопроса.
21

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

Майкл Майерс
источник
2
Вы всегда можете заставить каждый тип реализовывать любые статические методы интерфейса. Типы, кто-нибудь?
MichaelGG
16
Выйдите из себя и ответьте на вопрос: почему статические методы не могут быть переопределены? Если бы статические методы могли быть переопределены, как бы это выглядело? Что вы могли бы сделать с ними? Этот ответ в основном «Вы не можете, потому что вы не можете».
erickson
10

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

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

Согласно документу Java :

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

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

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

list.sort(ordering);

вместо

Collections.sort(list, ordering);

Пример статического метода (из самого документа ):

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));
    }    
}
akhil_mittal
источник
6

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

cliff.meyers
источник
Ясная, лаконичная логика. Хорошо сказано.
Оке Увечу
6

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

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

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

высокая девушка
источник
5

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

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

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}
Адриан Пронк
источник
5
  • Msgstr "Есть ли особая причина, по которой статические методы не могут быть переопределены".

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

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

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

Darron
источник
3

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

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

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

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

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

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

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

Мистер Блестящий и Новый 安 宇
источник
3

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

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

Флавио
источник
2

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

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

MichaelGG
источник
1
Какое отношение к этому имеют дженерики? Статический метод на интерфейсе все еще не может быть выполнен.
DJClayworth
Во-первых, это будет решение о реализации. Но я предполагаю, что он не хочет вызывать статические методы интерфейса (он мог бы просто использовать класс). Но вместо этого он хочет иметь что-то вроде класса типов или еще чего-нибудь над параметрами типа. Фактически, его последняя редакция показывает это еще более ясно.
MichaelGG
2
Why can't I define a static method in a Java interface?

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

Аникет Тхакур
источник
1

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

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

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

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

Шелби Мур III
источник
0

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

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

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

samoz
источник
2
Теоретически, вы можете определить интерфейс для включения статического поведения, то есть «реализации этого интерфейса будут иметь статический метод foo () с этой сигнатурой», и оставить реализацию вплоть до определенного класса. Я сталкивался с ситуациями, когда это поведение было бы полезно.
Роб
0

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

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

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

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

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

Handerson
источник
0

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

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

}
pvgoddijn
источник
1
Было бы напечатано «Я класс А». Тем не менее, если вы напечатаете, A.thisIsTheMethod()он напечатает «Я класс B».
cdmckay
но если вы вызываете методы интерфейса, как бы вы (или компилятор) узнали, какой метод следует вызвать? (помните, что может быть больше классов, в которых реализована Iface
pvgoddijn
извините, я хотел сказать: однако, если вы напечатаете, B.thisIsTheMethod()он напечатает "Я класс B".
cdmckay
я сказал IFace. Это метод специально, потому что в этом и заключается проблема. было бы невозможно вызвать его на интерфейсе без неопределенного поведения (даже если оно объявлено на нем)
pvgoddijn
0

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

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 из неизвестного класса времени компиляции! Динамическая диспетчеризация необходима, и вы можете переопределить статические методы (если не финальные) класса, расширив его (при вызове через статический интерфейс). Очевидно, что эти методы могут обращаться только к статическим переменным своего класса.

Кристоф Бушон
источник
0

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

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

Общее определение утилиты enum:

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, " , ")));
    }
}
Райан
источник
0

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

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

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

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

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

.. бла бла

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

beibichunai
источник
0

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

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

Аашиш Павар
источник
1
Это бесполезно, хотя. Определение статического метода в интерфейсе не требует дополнительных определений в классах, которые реализуют такой интерфейс. Если вы просто полностью удалите статический метод из интерфейса I, ваш код скомпилируется и запустится в любом случае без проблем. Другими словами, вы НЕ переопределяете основной метод интерфейса I в классе InterDemo, а просто создаете новый метод с той же сигнатурой.
Фран Марсоа
-2

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

MyImplClass.myMethod()

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

myInstance.myMethod()

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

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

MyImplClass.myMethod()

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

Павел Фельдман
источник
-5

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

VIckyb
источник