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

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

Вот пример:

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

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

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

IXMLizable.newInstanceFromXML(e);

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

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

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

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

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

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

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

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


person cdmckay    schedule 04.02.2009    source источник
comment
Вам не нужно загромождать определения методов (и полей) общедоступными в интерфейсах, кстати.   -  person Tom Hawtin - tackline    schedule 04.02.2009
comment
Хм, похоже, это дубликат stackoverflow.com/questions/21817/. Не видел этого раньше.   -  person Michael Myers    schedule 04.02.2009
comment
Не могли бы вы предоставить код, как бы вы хотели использовать методы статического интерфейса?   -  person Pavel Feldman    schedule 04.02.2009
comment
также обмануть stackoverflow.com/questions/129267/   -  person Epaga    schedule 04.02.2009
comment
@ Erickson, не могли бы вы проиллюстрировать на примере, где использование статического метода не работает в интерфейсе   -  person Deepak    schedule 09.10.2011
comment
Это будет возможно в Java 8: docs.oracle.com/ javase / tutorial / java / IandI /   -  person dakshang    schedule 16.02.2014
comment
@dakshang Да, но это не то, что хочет ОП.   -  person user253751    schedule 16.07.2015
comment
Вопрос @ JohnMercier сейчас закрыт. Теперь это настоящая нить   -  person lue    schedule 27.02.2020
comment
Это возможно в Java 8 - проверьте мой ответ ниже: stackoverflow.com/a/31162902/1216775   -  person akhil_mittal    schedule 12.11.2020


Ответы (24)


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

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

person erickson    schedule 04.02.2009
comment
Хороший звонок для Project Coin. mail.openjdk.java.net/pipermail/coin- dev / 2009-March / 000117.html - person Michael Myers; 09.03.2009
comment
Может ли причина №1 быть в множественном наследовании? Поскольку мы можем наследовать от нескольких интерфейсов, если два интерфейса содержали одну и ту же сигнатуру статического метода, а затем класс реализовал их оба и вызвал этот метод, тогда все могло усложниться так, что создатели языка Java хотели избежать, запретив наследование нескольких классов в первое место. Тот же аргумент, очевидно, можно было бы сделать для интерфейса, не позволяющего вообще определять какой-либо метод в них. - person shrini1000; 01.03.2011
comment
По тому же аргументу, даже полевые константы должны быть запрещены в интерфейсах, но, возможно, обработка их для множественного наследования не так сложна. - person shrini1000; 01.03.2011
comment
@ shrini1000 - Нет, статические методы разрешаются во время компиляции. С неоднозначностью можно справиться так же, как с помощью констант: с ошибкой компилятора. Однако предложение по Project Coin было отклонено, сославшись на некоторые непредвиденные трудности. Не уверен, что это было, но я не думаю, что это было в этом роде. - person erickson; 01.03.2011
comment
@ Erickson, не могли бы вы проиллюстрировать на примере, где Использование статического метода не работает в интерфейсе. - person Deepak; 09.10.2011
comment
@Deepak: Извините, я не совсем понимаю, что вы имеете в виду. Этот пост посвящен определению статических методов в интерфейсе. Вы говорите о присвоении результата статического метода (константному) полю интерфейса? - person erickson; 09.10.2011
comment
Я не могу дождаться, пока они это сделают. Вы уже можете объявить статические поля, так что вы можете создавать свои статические функции, завернутые в статические объекты, в качестве взлома. - person Paul Draper; 09.03.2013
comment
Правильно ли я понимаю, что Java 8 обращается к пункту 1., но не к пункту 2.? И, кстати, краткий ответ на пункт 2 - это оптимизация, потому что наличие статических переопределяемых методов в интерфейсах - вполне допустимое объектно-ориентированное требование (подумайте о поведении, общем для всех экземпляров каждого конкретного реализующего класса интерфейса, которое может использоваться как <T extends MyInterface> method(Class<T> cls){cls.staticMethod();//...} - person Mr_and_Mrs_D; 09.10.2013
comment
Да, Java 8 ничего не меняет в переопределении статических методов. Я не следую вашему примеру в том, как они будут использоваться. Но мне интересно, действительно ли Java 8 может вам помочь. Мне нужно будет прочитать об этом, но я думаю, что есть механизм реализации метода по умолчанию, который может решить то, к чему вы клоните. - person erickson; 10.10.2013
comment
Вы упомянули, что класс статичен. В Java это может быть правдой, но не обязательно. Есть много языков, которые занимают эту позицию (Smalltalk для одного из пионеров). Если подумать, есть много ситуаций, когда вы хотите, чтобы класс был динамичным. Если класс становится объектом, это совершенно новый мир, который предлагает вам и многие фреймворки DI почти становятся бесполезными. - person mathk; 21.01.2015
comment
Я думаю, что ответ следует обновить в соответствии с Java 8, чтобы вам не приходилось читать другие ответы, чтобы узнать полную картину - person kiedysktos; 25.04.2016
comment
@kiedysktos Я внес некоторые изменения. Вы это имели в виду? - person erickson; 25.04.2016
comment
да. Но теперь ваш 1 вопрос несовместим с этим :) вы говорите, что можете сделать X и следующую строку: 1. Почему вы не можете сделать X ?. Может быть, одна точка должна исчезнуть или выглядеть примерно так: Почему интерфейсы не могут содержать статические методы вплоть до Java 8? - person kiedysktos; 26.04.2016
comment
@kiedysktos Нет, вопрос №1 конкретно об интерфейсах, а вопрос №2 - о статических методах в целом. - person erickson; 26.04.2016
comment
нет, вы неправильно поняли. Я вижу противоречие между началом сообщения (в Java 8 интерфейсы могут иметь статические методы) и первой точкой (1. Почему интерфейсы не могут содержать статические методы). Теперь вы можете перечитать мой последний комментарий в этом контексте. Если можете, то какой смысл объяснять, почему вы не можете. Поэтому я предложил простую переформулировку 1. пункта. - person kiedysktos; 27.04.2016
comment
Указывает ли что-либо в этом обсуждении на то, что включение статических интерфейсов в Java 8 не затрагивает реализацию концепции соглашения, которую хотел OP? Или я неправильно понял и этот разговор, и ОП? Мне бы понравилась версия статических методов с принудительным исполнением кода, даже если бы она [гротескно] требовала нового ключевого слова. Что-то вроде interface ClassReset{musthave public static void RESET();} - person ; 02.09.2016
comment
@ tgm1024 Да, раздел "Интерфейсы" конструктора объясняет, почему не имеет смысла пытаться вызвать полиморфное поведение через тип, известный во время компиляции. Как бы вы вызывали RESET() в данном классе? Вы бы написали SomeClass.RESET(). Таким образом, вам не нужен интерфейс для описания этого API; это статично. Интерфейсы используются, когда вы не знаете конкретный тип во время компиляции. Этого никогда не бывает со статическим методом. - person erickson; 02.09.2016
comment
@erickson, я читал это, но это не относится к тому, что я написал - я вообще не ищу полиморфного поведения. Я ищу принудительное исполнение кода (примечание: не исполнение контракта в полиморфном смысле). Я хочу убедиться, что есть public static RESET(). В этом нет ничего полиморфного: это статический метод - член класса. Поэтому я предположил, что могу даже смириться с новым, хотя и гротескным, ключевым словом. должен быть способ сказать, что этот класс должен иметь определенные статические методы. Возможно, @MustHavethen, если это не соответствует собственно языку. - person ; 03.09.2016
comment
@ tgm1024 Да, я полностью понял, что вы ищете. Я упомянул полиморфизм, потому что интерфейсы только описывают операции экземпляра. Я знаю, что это тавтологический ответ на вопрос о том, почему интерфейсы не могут иметь статические методы, поэтому есть еще одна половина моего ответа: статические методы интерфейса не имеют смысла и бесполезны. Интерфейсы полезны для экземпляров, потому что код написан с этими интерфейсами как ссылочные типы, то есть вы пишете код, который вызывает run() в экземпляре Runnable. Вы не можете вызывать Resettable.RESET(). Вам понадобится тип среды выполнения. - person erickson; 05.09.2016
comment
@erickson, я полагаю, что я возражаю против того, что это должно было быть ясно из того, что я написал изначально, и хотя вы, кажется, поняли это, мне показалось, что вы не ответили. Обеспечение соблюдения соглашения о кодировании было одной из целей OP OP. В моем случае я ищу (статическое something) требование кодирования, которое должно выполняться внутри класса; конечно ничего полиморфного по своей природе. @ThouShaltHaveThisThing public static int HOOZIT и @ThouShaltHaveThisThing public static int WAZZIT(int bla), чтобы прояснить суть дела. Тем не менее, спасибо за ответ. - person ; 05.09.2016
comment
@ tgm1024 Да, я тоже понял, но я удалил эту часть, потому что мой комментарий был слишком длинным, и я надеялся, что объяснение того, какие интерфейсы подходят, подразумевает, что то, чем они не являются < / i> good for было бы достаточно ясно. Но, если уточнить это, интерфейсы никогда не предназначались для обеспечения соблюдения соглашений о кодировании. - person erickson; 06.09.2016
comment
Строительство - это часть реализации, а не интерфейс. Любой код, который успешно работает с интерфейсом, не заботится о конструкторе. - Это явно неправда. На других языках (например, Swift) я могу создавать новые экземпляры T, не зная T статически, потому что я обещаю в интерфейсе, что определенный конструктор (или статический метод) будет существовать во время выполнения. Тот факт, что генерацию невозможно указать в Java, не означает, что это не имеет смысла. - person Raphael; 07.03.2018
comment
Любой код, который заботится о конструкторе, в любом случае должен знать конкретный тип, а интерфейс можно игнорировать. - с такой логикой интерфейсы всегда можно игнорировать, не так ли? Почему забота о конструкторах / генераторах отличается от заботы о методах? - person Raphael; 07.03.2018
comment
@Raphael Хорошие моменты в вашем первом комментарии. Конечно, Java поддерживает другие шаблоны создания, чтобы отделить создание экземпляров от реализации. Возможно, мне придется перефразировать некоторые из этих слов. В вашем примере Swift T является параметром универсального типа кода, выполняющего создание экземпляра? Конечно, стирание типов в Java не упрощает, но есть некоторые уловки, которые можно использовать для достижения чего-то подобного. Я не совсем понимаю ваш второй комментарий. Из этого не следует, что тип внедренного объекта можно игнорировать. - person erickson; 07.03.2018
comment
@erickson Да, T может быть параметром типа; см. например здесь. Точно так же вам могут потребоваться статические свойства и функции класса или структуры Swift. Не то, чтобы система типов Swift идеальна - есть серьезные недостатки вокруг универсальных шаблонов - но эта часть очень полезна. Я не вижу никаких концептуальных оснований против этого в Java; это чисто функция компилятора. Требование самих конструкторов и статических членов, то есть; стирание дженериков - другая проблема. - person Raphael; 07.03.2018
comment
@Raphael: большую часть этой проблемы можно решить в Java, определив дополнительный фабричный интерфейс. Когда его единственный фабричный метод совпадает с конструктором реализации, он может быть реализован так же просто, как ImplementationType::new или через ImplementationType::factoryMethodName. Использование этого шаблона требует, чтобы методы принимали только другой параметр для фабрики, когда это необходимо. Это немного более подробно, чем фабричные методы, требуемые интерфейсом, с другой стороны, он предлагает гораздо большую гибкость, например возможность определять, реализовывать и использовать фабрики, отличные от заявленных в интерфейсе. - person Holger; 21.08.2018

Об этом уже спросили и ответили: здесь

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

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

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

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

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

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

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

person DJClayworth    schedule 04.02.2009
comment
Если вы используете ‹T extends MyInterface› в качестве параметра универсального типа, было бы неплохо гарантировать через интерфейс, что T может .doSomething (). - person Chris Betti; 24.09.2013
comment
Хотя я понимаю аргументы, я согласен с @Chris_Betti (даже для неуниверсальных типов): было бы хорошо, если бы структура кода гарантировала, что некоторые классы реализуют определенный статический API. Может быть, можно использовать другую концепцию ... - person Juh_; 16.07.2015
comment
@Juh_, ..... или новое ключевое слово, если это абсолютно необходимо. Я считаю, что static - это в любом случае хреновый термин в языках, и он и так зашел слишком далеко. Так что иметь это само по себе уже отрывочно. См. Мой пример выше stackoverflow.com/questions/512877/ {shrug}. - person ; 02.09.2016
comment
Это кажется неправдой: никогда не стоит объявлять статический метод в интерфейсе. Если у меня есть набор классов, которые без создания экземпляров могут предложить мне некоторую информацию, но мне понадобится общий интерфейс для размещения этой статической информации уровня класса (т.е. интерфейс с переопределяемым статическим методом), тогда это допустимое использование . Подумайте об отражении ++, где вы можете собирать метаинформацию о свойствах класса, не прибегая к атрибутам, отражению и т. Д. - person Jason; 16.03.2017
comment
Нет смысла объявлять статический метод в интерфейсе. Иногда статические методы в интерфейсе имеют смысл, потому что они обеспечивают более функциональный подход. Статические методы в интерфейсах не могут изменять статическое состояние интерфейса, потому что в интерфейсе вообще нет состояния. - person Piohen; 24.11.2017
comment
Нет смысла объявлять статический метод в интерфейсе. Это неправда: представьте, что ваша система имеет преобразователь классов по умолчанию. Если он обнаруживает, что вы реализуете метод ContainerInjectionInterce :: create (Container $ container), то он, например, создаст объект с этой функцией. - person user3790897; 06.07.2018

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

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

public interface IXMLizable {
  public Element toXMLElement();
}
person Peter Lawrey    schedule 13.02.2009
comment
+1 заводская выкройка звучит как решение проблемы. (хотя не к вопросу) - person pvgoddijn; 20.02.2009
comment
Может ли кто-нибудь сказать мне, что означает размещение здесь ‹T extends IXMLizable›. Я новичок в java. Что это дает? - person Nuwan Harshakumara Piyarathna; 06.05.2020
comment
@NuwanHarshakumaraPiyarathna T должен быть классом, расширяющим IXMLizable. Изучите дженерики Java, чтобы лучше понять, что это означает. - person adrian; 17.05.2020

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

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

person rogue lad    schedule 26.11.2014
comment
Это прекрасный пример этого вопроса. - person ; 18.02.2018

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

person Michael Myers    schedule 04.02.2009
comment
Вы всегда можете заставить каждый тип реализовать любые методы статического интерфейса. Классы типов, кто-нибудь? - person MichaelGG; 04.02.2009
comment
Выйдите за пределы себя и ответьте на вопрос: Почему нельзя переопределить статические методы? Если бы статические методы можно было переопределить, как бы это выглядело? Что ты мог с ними сделать? По сути, это ответ: вы не можете, потому что не можете. - person erickson; 04.02.2009

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

На самом деле это можно сделать в Java 8.

Согласно Java doc:

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

В 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));
    }    
}
person akhil_mittal    schedule 01.07.2015
comment
Приятно знать, что эти классы Util можно заменить статическими методами в самом интерфейсе. - person Gaurav; 26.08.2020

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

person cliff.meyers    schedule 04.02.2009
comment
Четкая, лаконичная логика. Хорошо сказано. - person Oke Uwechue; 15.08.2019

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

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

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

person tallgirl    schedule 05.07.2011

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

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

public interface Test {
    static class Inner {
        public static Object get() {
            return 0;
        }
    }
}
person Adrian Pronk    schedule 04.02.2009

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

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

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

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

person Darron    schedule 04.02.2009

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

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

person flavio    schedule 15.06.2016

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

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

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

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

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

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

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

person Mr. Shiny and New 安宇    schedule 04.02.2009

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

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

person Aniket Thakur    schedule 20.08.2013

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

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

person MichaelGG    schedule 04.02.2009
comment
При чем здесь дженерики? Статический метод интерфейса по-прежнему будет невыполнимым. - person DJClayworth; 05.02.2009
comment
Во-первых, это решение о реализации. Но я предполагаю, что он не хочет вызывать статические методы в интерфейсе (он мог бы просто использовать класс). Но вместо этого хочет иметь что-то вроде класса типов или еще чего-то, кроме параметров типа. Фактически, его последняя редакция показывает это еще яснее. - person MichaelGG; 05.02.2009

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

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

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

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

person Shelby Moore III    schedule 14.02.2011

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

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

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

person samoz    schedule 04.02.2009
comment
Что ж, теоретически вы можете определить интерфейс для включения статического поведения, то есть реализации этого интерфейса будут иметь статический метод foo () с этой сигнатурой, а реализацию оставить на усмотрение конкретного класса. Я сталкивался с ситуациями, когда такое поведение было бы полезно. - person Rob; 04.02.2009

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

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

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

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

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

person Handerson    schedule 04.02.2009

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

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

}
person pvgoddijn    schedule 13.02.2009
comment
Он напечатал бы I'm class A. Однако, если бы вы набрали A.thisIsTheMethod(), он напечатал бы I'm class B. - person cdmckay; 15.02.2009
comment
но если вы вызываете методы интерфейса, как бы вы (или компилятор) узнали, какой метод следует вызвать? (помните, что может быть больше классов, аккуратно реализующих Iface - person pvgoddijn; 16.02.2009
comment
извините, я хотел сказать: однако, если вы наберете B.thisIsTheMethod(), будет напечатано, что я класс B. - person cdmckay; 19.02.2009
comment
Я специально сказал IFace.ThisIsTHeMethod, потому что в этом и заключается проблема. было бы невозможно вызвать его в интерфейсе без неопределенного поведения (даже если он объявлен на нем) - person pvgoddijn; 20.02.2009

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

person Christophe Bouchon    schedule 03.10.2013

Хотя я понимаю, что 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
}

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

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, " , ")));
    }
}
person Ryan    schedule 20.05.2015

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

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

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

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

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

..бла-бла

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

person beibichunai    schedule 19.09.2015

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

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

person Aashish Pawar    schedule 29.10.2017
comment
Но это бесполезно. Определение статического метода в интерфейсе не требует дополнительных определений в классах, реализующих такой интерфейс. Если вы просто полностью удалите статический метод из интерфейса I, ваш код все равно компилируется и запускается без проблем. Другими словами, вы НЕ переопределяете основной метод интерфейса I в классе InterDemo, а просто создаете новый метод с той же сигнатурой. - person Fran Marzoa; 26.10.2018

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

MyImplClass.myMethod()

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

myInstance.myMethod()

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

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

MyImplClass.myMethod()

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

person Pavel Feldman    schedule 04.02.2009

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

person VIckyb    schedule 04.04.2013