Вопрос по java – Необязательные методы в интерфейсе Java

101

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

Я заметил, что в некоторых интерфейсах, таких как интерфейс Collection, есть методы, которые комментируются как необязательные, но что именно это означает? Меня это немного смутило, так как я думал, что все методы, указанные в интерфейсе, потребуются?

Какие методы ты имеешь в виду? Я не могу найти его в JavaDoc или исходном коде dcpomero

Ваш Ответ

12   ответов
205

Язык Java требует, чтобы каждый метод в интерфейсе был реализован каждой реализацией этого интерфейса. Период. Нет никаких исключений из этого правила. Сказать, что «коллекции - исключение», говорит о том, что на самом деле все происходит нечетко.

Важно понимать, что существует два уровня соответствия интерфейсу:

Что может проверить язык Java. Это в значительной степени сводится к: есть линескольк реализация для каждого из методов?

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

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

При разработке API коллекций Джошуа Блох решил, что вместо очень тонкодисперсных интерфейсов, позволяющих различать различные варианты коллекций (например, читаемые, записываемые, произвольный доступ и т. Д.), У него будет только очень грубый набор интерфейсов, в первую очередьCollection, List, Set а такжеMap, а затем задокументируйте определенные операции как «необязательные». Это должно было избежать комбинаторного взрыва, который мог бы произойти из-за мелкозернистых интерфейсов. От Часто задаваемые вопросы по дизайну API коллекций Ja:

Чтобы проиллюстрировать проблему более подробно, предположим, что вы хотите добавить понятие модифицируемости в Иерархию. Вам нужны четыре новых интерфейса: ModifiableCollection, ModifiableSet, ModifiableList и ModifiableMap. То, что раньше было простой иерархией, теперь является грязной гетерархией. Кроме того, вам нужен новый интерфейс Iterator для использования с немодифицируемыми коллекциями, который не содержит операцию удаления. Теперь вы можете покончить с UnsupportedOperationException? К сожалению нет

Рассматривать массивы. Они реализуют большинство операций List, но не удаляют и не добавляют. Это списки фиксированного размера. Если вы хотите зафиксировать это понятие в иерархии, вам нужно добавить два новых интерфейса: VariableSizeList и VariableSizeMap. Вам не нужно добавлять VariableSizeCollection и VariableSizeSet, потому что они будут идентичны ModifiableCollection и ModifiableSet, но вы можете все равно добавить их для согласованности. Кроме того, вам нужен новый вариант ListIterator, который не поддерживает операции добавления и удаления, чтобы сопровождать неизменяемый список. Теперь у нас до десяти или двенадцати интерфейсов плюс два новых интерфейса Iterator вместо наших первоначальных четырех. Мы все? Нет.

урналы @Consider (такие как журналы ошибок, журналы аудита и журналы для восстанавливаемых объектов данных). Это естественные последовательности только для добавления, которые поддерживают все операции со списками, кроме операций удаления и установки (замены). Они требуют нового основного интерфейса и нового итератора.

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

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

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

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

Обратите внимание, потому чтоUnsupportedOperationException этоRuntimeException вы можете выбросить его из любой реализации метода, если речь идет о компиляторе. Например, вы можете выбросить его из реализацииCollection.size(). Однако такая реализация будет нарушать договор в качестве документации дляCollection.size() не говорит, что это разрешено.

Aside: подход, используемый в Java API Collections, несколько спорен (однако, теперь он менее вероятен, чем когда он был впервые представлен). В идеальном мире интерфейн имеют необязательные операции, и вместо этого будут использоваться мелкозернистые интерфейсы. Проблема в том, что Java не поддерживает ни предполагаемые структурные типы, ни типы пересечений, поэтому попытка сделать все «правильным образом» в конечном итоге становится чрезвычайно громоздкой в случае коллекций.

+ 1 дляThere are no exceptions to this rule. Хотите знать, почему этот ответ не помечен как принятый. Другие хороши, но вы дали более чем достаточно. xyz
«Язык Java требует, чтобы каждый метод в интерфейсе был реализован каждой реализацией этого интерфейса. Точка. Из этого правила нет исключений». За исключением ... когда есть. :-) Интерфейсы Java 8 могут указывать реализацию метода по умолчанию. Таким образом, в Java 8 ... Неверно, что каждый метод в интерфейсе должен быть РЕАЛИЗОВАН каждой реализацией интерфейса, по крайней мере, не в том смысле, что вы должны код реализации в конкретном классе. DaBlick
@ DaBlick Когда я сказал «реализуется каждой реализацией», я не имел в виду, что указанная реализация метода должна находиться в источнике реализующего класса. Даже до Java 8 можно унаследовать реализацию метода интерфейса, даже от класса, который не реализует указанный интерфейс. например: создатьFoo это не реализуетRunnable публичным методомvoid run(). Теперь создайте классBar чтоextends Foo а такжеimplements Runnable без перегрузкиrun. Он все еще реализует метод, хотя и косвенно. Аналогично, реализация метода по умолчанию все еще является реализацией. Laurence Gonsalves
Apologies. Я не пытался быть педантично критичным настолько, чтобы привлечь внимание к функции Java 8, которая могла бы иметь отношение к оригинальному посту. В Java 8 у вас теперь есть возможность иметь реализации, которые не закодированы в ЛЮБОМ суперклассе или подклассе. Это (ИМХО) открывает новый мир шаблонов проектирования, включая те, которые могут быть уместны в тех случаях, когда запрет на множественное наследование мог бы создать некоторые проблемы. Я думаю, что это породит новый набор очень полезных шаблонов дизайна. \ DaBlick
@ AndrewS, потому что в Java 8remove была задана реализация по умолчанию. Если вы не реализуете его, то ваш класс получит реализацию по умолчанию. Два других упомянутых вами метода не имеют реализаций по умолчанию. Laurence Gonsalves
25

- все методы должны быть реализованы.

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

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

Хороший пример-add() метод для неизменяемых коллекций - бетон просто реализует метод, который ничего не делает, кроме броскаNotSupportedException

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

Обновить

Как в Java 8, метод по умолчанию был представлен

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

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

Вместо того, чтобы «не испортить», я думаю, что это больше «просто так». user166390
@ pst: I Верю Это то, о чем думали дизайнеры, когда внедряли его, но я не знаю наверняка. Ясчитат любой другой подход просто создаст беспорядок, но опять же - может быть ошибочным. Вот что я пытался показать здесь: этот пример - исключение, а не обычное - и хотя иногда это может быть полезно - для общего случая - его следует избегать, если это возможно. amit
"не должен реализовывать это (вероятно, он просто создаст метод, который выбрасывает ...)". Чтоявляетс внедрение метода. user207421
Поскольку, к сожалению, это был принятый ответ, я бы предложил переписать его. 'Обычн, реализующий класс должен (и будет) реализовывать все методы, 'вводит в заблуждение как EJP уже указал. Alberto
«Необязательный» в коллекции означает, что реализующий класс не должен его реализовывать. »- это просто ложь. Под« не нужно реализовывать »вы подразумеваете что-то еще. djechlin
17

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

interface Foo {
  void doSomething();
  void doSomethingElse();
}

class MyClass implements Foo {
  public void doSomething() {
     /* All of my code goes here */
  }

  public void doSomethingElse() {
    // I leave this unimplemented
  }
}

Теперь я ушелdoSomethingElse() unimplemented, оставляя его свободным для реализации моими подклассами. Это необязательно.

class SubClass extends MyClass {
    @Override
    public void doSomethingElse() {
      // Here's my implementation. 
    }
}

Однако, если вы говорите об интерфейсах Collection, как уже говорили другие, они являются исключением. Если определенные методы не реализованы и вы их вызываете, th, ey может броситьUnsupportedOperationException исключения.

Я могу поцеловать тебя, мой друг. Micro
16

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

некоторых реализаций коллекции есть ограничения на элементы, которые они могут содержать. Например, некоторые реализации запрещают нулевые элементы, а некоторые имеют ограничения на типы их элементов. Попытка добавить неприемлемый элемент вызывает неконтролируемое исключение, обычно NullPointerException или ClassCastException. Попытка запросить наличие неприемлемого элемента может вызвать исключение или просто вернуть false; некоторые реализации будут демонстрировать первое поведение, а некоторые - второе. В более общем смысле, попытка выполнить операцию с неподходящим элементом, завершение которого не приведет к вставке неприемлемого элемента в коллекцию, может вызвать исключение или может быть успешным, по выбору реализации. Такие исключения помечены как «необязательные» в спецификации для этого интерфейса.

Я никогда не понимал, что javadocs подразумевает под необязательным. Я считаю, что они имели в виду, как вы сказали. Но большинство методов необязательны по этому стандарту:new Runnable ( ) { @ Override public void run ( ) { throw new UnsupportedOperationException ( ) ; } }; emory
Это не относится к оптимальные методы, а скорее, например,add((T)null) может быть действительным в одном случае, нон другой. То есть речь идет о необязательных исключениях / поведении и для аргументов («ограничения на элементы» ... «недопустимый элемент« ... »исключения помечены как необязательные») и не адрес оптимальные методы. user166390
9

у которыхdefault реализации в Java 8+), но реализация не должна делать ничего функционально полезного. В частности, это:

Может быть пустым (пустой метод.) Можно просто броситьUnsupportedOperationException (или похожие

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

4

Если мы пройдем код AbstractCollection.java в grepCode, который является классом-предком для всех реализаций коллекции, он поможет нам понять значение необязательных методов. Вот код для метода add (e) в классе AbstractCollection. метод add (e) является необязательным согласноколлекци интерфейс

public boolean  add(E e) {

        throw new UnsupportedOperationException();
    } 

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

4

но теперь он более нюансированный.

Во-первых, эти утверждения из принятого ответа остаются правильными:

интерфейсы предназначены для указания их неявного поведения в контракте (формулировка правил поведения, которым должны следовать реализующие классы, чтобы считаться действительными) Существует различие между контрактом (правилами) и реализацией (программное кодирование правил) методы, указанные в интерфейсе, ДОЛЖНЫ быть ВСЕГДА реализованы (в какой-то момент)

Так что же нового в Java 8? Говоря о "Необязательные методы" любое из следующего теперь подходит:

1. Метод, реализация которого не является обязательной по контракт

«Третье утверждение» говорит, что абстрактные методы интерфейса всегда должны быть реализованы, и это остается верным в Java 8+. Однако, как и в Java Collections Framework, некоторые абстрактные методы интерфейса можно описать в контракте как «необязательные».

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

public SomeReturnType optionalInterfaceMethodA(...) {
    throw new UnsupportedOperationException();
}

В Java 7 и более ранних версиях это был действительно единственный вид «необязательного метода», то есть метод, который, если не реализован, вызывал исключение UnsupportedOperationException. Это поведение обязательно определяется интерфейсным контрактом (например, необязательные методы интерфейса Java Collections Framework).

2. Метод по умолчанию, повторная реализация которого является необязательной

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

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

В этой новой среде Java Collections Frameworkможет быт переписано как:

public interface List<E> {
    :
    :
    default public boolean add(E element) {
        throw new UnsupportedOperationException();
    }
    :
    :
}

Таким образом, «необязательный» методadd() имеет поведение по умолчанию, генерирующее исключение UnsupportedOperationException, если реализующий класс не предоставляет своего собственного нового поведения, которое именно то, что вы хотели бы, и которое соответствует контракту для List. Если автор пишет класс, который не позволяет добавлять новые элементы в реализацию List, реализацияadd() не является обязательным, потому что поведение по умолчанию именно то, что нужно.

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

3. Метод, который возвращаетOptional результат

Последний новый тип необязательного метода - это просто метод, который возвращаетOptional.Optional class обеспечивает значительно более объектно-ориентированный способ работы сnull полученные результаты

В свободном стиле программирования, таком как тот тип, который обычно наблюдается при кодировании с новым API Java Streams, нулевой результат в любой момент приводит к аварийному завершению программы с NullPointerException.Optional class предоставляет механизм для возврата нулевых результатов в код клиента таким образом, чтобы обеспечить свободный стиль без сбоя клиентского кода.

3

я вдохновлен SurfaceView.Callback2. Я думаю, что это официальный способ

public class Foo {
    public interface Callback {
        public void requiredMethod1();
        public void requiredMethod2();
    }

    public interface CallbackExtended extends Callback {
        public void optionalMethod1();
        public void optionalMethod2();
    }

    private Callback mCallback;
}

Если вашему классу не нужно реализовывать дополнительные методы, просто «реализует обратный вызов». Если вашему классу нужно реализовать необязательные методы, просто "реализует CallbackExtended".

Извините за дерьмо на английском.

3

эта тема была адресована ... да ... но думаю, один ответ отсутствует. Я говорю о «Методах по умолчанию» интерфейсов. Например, давайте представим, что у вас будет класс для закрытия чего-либо (например, деструктора или чего-то еще). Допустим, у него должно быть 3 метода. Давайте назовем их «doFirst ()», «doLast ()» и «onClose ()».

Так что мы говорим, что хотим, чтобы любой объект этого типа по крайней мере реализовал «onClose ()», но другие являются необязательными.

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

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

public interface Closer {
    default void doFirst() {
        System.out.print("first ... ");
    }
    void onClose();
    default void doLast() {
        System.out.println("and finally!");
    }
}

Что бы сейчас произошло, если бы вы, например, реализовали это в классе с именем «Test», компилятор прекрасно справился бы со следующим:

public class TestCloser implements Closer {
    @Override
    public void onClose() {
        System.out.print("closing ... ");
    }
}

с выходом:

first ... closing ... and finally!

ил

public class TestCloser implements Closer {
    @Override
    public void onClose() {
        System.out.print("closing ... ");
    }

    @Override
    public void doLast() {
        System.out.println("done!");
    }
}

с выходом:

first ... closing ... done!

Все комбинации возможны. Все, что имеет «по умолчанию», может быть реализовано, но не должно, но все, что не должно быть реализова

Надеюсь, это не совсем неправильно, что я сейчас отвечу.

Всем хорошего дня!

[edit1]: Обратите внимание: это работает только в Java 8.

Спасибо за помощь Thorben AndroidLover
Это работает только на Java 8? * AndroidLover
Да, извини, я забыл упомянуть об этом ... Нужно отредактировать. Thorben Kuck
0

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

0

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

Вместо того, чтобы использовать интерфейс, я использовал класс с пустой реализацией, такой как:

public class MyCallBack{
    public void didResponseCameBack(String response){}
}

И вы можете установить переменную-член CallBack следующим образом,

c.setCallBack(new MyCallBack() {
    public void didResponseCameBack(String response) {
        //your implementation here
    }
});

тогда назови это так.

if(mMyCallBack != null) {
    mMyCallBack.didResponseCameBack(response);
}

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

0

Учебник по коллекциям Java в Oracle:

Чтобы поддерживать количество управляемых интерфейсов коллекции, платформа Java не предоставляет отдельных интерфейсов для каждого варианта каждого типа коллекции. (Такие варианты могут включать неизменяемый, фиксированный размер и только добавление.) Вместо этого операции модификации в каждом интерфейсе обозначаются как необязательные - данная реализация может решить не поддерживать все операции. Если вызвана неподдерживаемая операция, коллекция выдает символ UnsupportedOperationException. Реализации отвечают за документирование, какие из необязательных операций они поддерживают. Все реализации общего назначения платформы Java поддерживают все необязательные операции.

Похожие вопросы