Дополнительные методы в интерфейсе Java

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

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

Ответ 1

В ответах здесь, по-видимому, очень много путаницы.

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

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

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

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

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

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

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

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

Рассмотрим журналы (например, журналы ошибок, журналы аудита и журналы для восстанавливаемые объекты данных). Они являются естественными последовательностями только для присоединения, которые поддерживают все операции List, кроме удаления и установки (Заменить). Им нужен новый интерфейс ядра и новый итератор.

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

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

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

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

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

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

Ответ 2

Чтобы скомпилировать реализацию (не абстрактного) класса для интерфейса - все методы должны быть реализованы.

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

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

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

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


Update:

По состоянию на java 8 был введен метод

Ответ 3

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

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

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

Ответ 4

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

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

Ответ 5

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

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

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

Ответ 6

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

public boolean  add(E e) {

        throw new UnsupportedOperationException();
    } 

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

Ответ 7

В Java 8 и более поздних версиях ответ на этот вопрос остается в силе, но теперь он более нюансирован.

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

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

Итак, каков нюанс, который является новым в Java 8? Говоря о "Необязательных методах", любое из следующих методов теперь apt:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Ответ 8

На самом деле, меня вдохновляет 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".

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

Ответ 9

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

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

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!

Возможны все комбинации. Все, что имеет "default", может быть реализовано, но не должно, однако ничего не должно быть реализовано.

Надеюсь, что это не совсем так, что я сейчас отвечаю.

У нас отличный день!

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

Ответ 10

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

Ответ 11

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

Итак, вместо использования интерфейса я использовал класс с пустой реализацией, например:

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

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

Ответ 12

Учебник по сборкам Oracle Java:

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