в интерфейсах нельзя описывать публичные константы
Поля в интерфейсах
У меня есть основной вопрос на Java, но это общий вопрос в ООП. Почему интерфейсы позволяют устанавливать поля? Разве это не противоречит тому, что должен делать интерфейс?
Как я понял, интерфейс-это то, что в английском языке было бы прилагательным. Итак, если мой класс реализует интерфейсы Runnable и Serializable, я гарантирую пользователю, что мой класс удовлетворит условиям Runnable и Seriablizable. Однако это означает, что интерфейсы «без гражданства», но им разрешено иметь поля в Java.
Я что-то пропустила?
4 ответов
обычно рекомендуется избегать таких интерфейсов, но иногда вы можете найти интерфейс, который не имеет методов и используется только для хранения списка постоянных значений.
прежде всего, есть разница между парадигмы ООП и реализация ООП в Java, так же слова могут означать немного разные вещи.
Java более или менее следует этой идее, но, как и любая реализация парадигмы, имеет свои особенности. Java позволяет описывая методы, то есть действия, которые реализующий объект должен иметь возможность выполнять, но не какие-либо детали реализации, таким образом, ничего об объектных полях или частных методах.
а насчет константы ( public final static поля)? Являются ли они частью реализации или интерфейса. Это могут быть как. Е. Г. интерфейс «программист» может иметь постоянный WORK_HOURS значение «8». Таким образом, Java позволяет описывать константы и в интерфейсах.
обратите внимание, что Java только помогает вы, чтобы сделать хороший дизайн ООП, но это не сильно требует этого. В частности, не все публичные методы объекта должны существовать в интерфейсе. Например, методы getter и setter обычно являются общедоступными, но на самом деле они являются части реализации, не интерфейс, и поэтому не стоит вводить их в интерфейс.
(обратите внимание, что большинство вещей, которые я описал здесь, относятся к mainstream ООП, как в Java, но есть и другие виды ООП, такие как прототип на основе одного, в частности, реализован в JavaScript).
что, если этот интерфейс относится к константам? Разве не было бы естественно объявить их в интерфейсе?
Да, у вас могут быть постоянные поля в интерфейсах, но вы правы, когда говорите, что «это противоречит тому, что должен делать интерфейс», поскольку это не очень хорошая практика. Почему вы хотите, чтобы все ваши классы реализовывали интерфейс с одинаковыми константами? Вы можете просто иметь их в классе, который их использует, или, если вам действительно нужно их как-то экспортировать, иметь их в отдельном классе utiliy, как это:
у вас также есть перечисления, если вам нужно представляют собой набор постоянных полей с определенным значением. Я не вижу никакого «варианта использования», где вам действительно нужны константы в интерфейсе. Но может быть неправильно:)
Интерфейсы
Ключевое слово interface используется для создания полностью абстрактных классов. Создатель интерфейса определяет имена методов, списки аргументов и типы возвращаемых значений, но не тела методов.
Наличие слова interface означает, что именно так должны выглядеть все классы, которые реализуют данный интерфейс. Таким образом, любой код, использующий конкретный интерфейс, знает только то, какие методы вызываются для этого интерфейса, но не более того.
Чтобы создать интерфейс, используйте ключевое слово interface вместо class. Как и в случае с классами, вы можете добавить перед словом interface спецификатор доступа public (но только если интерфейс определен в файле, имеющем то же имя) или оставить для него дружественный доступ, если он будет использоваться только в пределах своего пакета. Интерфейс может содержать поля, но они автоматически являются статическими (static) и неизменными (final). Все методы и переменные неявно объявляются как public.
Класс, который собирается использовать определённый интерфейс, использует ключевое слово implements. Оно указывает, что интерфейс лишь определяет форму, а вам нужно наполнить кодом. Методы, которые реализуют интерфейс, должны быть объявлены как public.
Интерфейсов у класса может быть несколько, тогда они перечисляются за ключевым словом implements и разделяются запятыми.
Интерфейсы могут вкладываться в классы и в другие интерфейсы.
Если класс содержит интерфейс, но не полностью реализует определённые им методы, он должен быть объявлен как abstract.
Интерфейсы — это не классы. С помощью ключевого слова new нельзя создать экземпляр интерфейса:
Но можно объявлять интерфейсные переменные:
При этом интерфейсная переменная должна ссылаться на объект класса, реализующего данный интерфейс.
Рассмотрим быстрый пример создания интерфейса. Выберите в меню File | New | Interface и придумайте имя для нового интерфейса. В полученной заготовке добавьте два имени метода (только имена, без кода).
Создайте или откройте какой-нибудь класс, к которому нужно применить интерфейс, и добавьте к нему implements SimpleInterface. Среда разработки подчеркнёт красной линией имя класса и предложит добавить методы, которые требуются интерфейсом. Соглашаемся и получаем результат:
Среда разработки сгенерировала два метода и использовала в качестве возвращаемых результатов значения по умолчанию. Это могут быть и нулевые значения и null. Осталось подправить шаблоны созданных методов под свои задачи. Например, так:
Здесь важно понять роль интерфейса. Мы лишь придумываем имена, а класс уже реализует нужную задачу. Для примера можно создать в интерфейсе метод play() для класса Пианино и класса Гитара, так как играть можно на обеих инструментах. Но код в методах будет отличаться, так как принцип игры на инструментах совершенно разный.
Константы в интерфейсах
Интерфейсы можно использовать для импорта констант в несколько классов. Вы просто объявляете интерфейс, содержащий переменные с нужными значениями. При реализации интерфейса в классе имена переменных будут помещены в область констант. Поля для констант становятся открытыми и являются статическими и конечными (модификаторы public static final). При этом, если интерфейс не будет содержать никаких методов, то класс не будет ничего реализовывать. Хотя данный подход не рекомендуют использовать.
Расширение интерфейсов
Интерфейс может наследоваться от другого интерфейса через ключевое слово extends.
Методы обратного вызова
Интерфейсы часто используются для создания методов обратного вызова (callback). Рассмотрим такой пример. Создадим новый класс SubClass с интерфейсом MyCallback:
У интерфейса мы определили один метод callBackReturn(). Далее в классе мы создали объект интерфейса и инициализировали его в конструкторе класса. В классе также был создан метод doSomething(), в котором может содержаться какой-то сложный код. В конце метода вызывается метод интерфейса. В данном случае мы сами создали метод и знаем его код. Но во многих случаях, вы будете использовать готовый метод какого-то класса и вы не будете знать, что именно содержится в этом методе. Вам надо только знать, что такой метод существует, например, из документации и он выполняет конкретную задачу.
Переходим в код активности и подключаем интерфейс через ключевое слово implements:
Среда разработки поможет вставить шаблон метода интерфейса.
Теперь мы можем использовать метод обратного вызова callBackReturn() для решения своих задач. Допустим у нас есть текстовая метка и кнопка. При щелчке выполняется какой-то сложный код из класса SubClass. Когда он закончит работу, то сработает метод обратного вызова callBackReturn(), в котором пропишем нужные действия.
Слушатели
Очень часто для интерфейса используют слово Listener, например, у кнопки есть интерфейс OnClickListener.
Мы можем создавать подобные слушатели для собственных классов.
Также интерфейсы часто используются при работе с фрагментами.
Требуется определённая практика и опыт, чтобы быстро разбираться в коде с использованием интерфейсов, так как приходится отслеживать цепочку вызовов из разных классов. Но бояться их не нужно.
Java 8 интерфейсы
Интерфейсы в Java — это некоторый контракт, описание методов, которые обязательно должны присутствовать в классе, реализующем этот интерфейс. Интерфейсы позволяют иметь несколько различных реализаций одного и того же действия, но выполняемого различными способами или с различными видами данных. Когда вы пишете какую-либо библиотеку, имеет смысл давать пользователям работать только с публичными интерфейсами. Тогда пользователи смогут заменить одну реализацию этих интерфейсов на другую без переписывания большей части кода, также вы сможете менять внутреннюю архитектуру библиотеки без необходимости переписывания зависящего клиентского кода.
Интерфейсы в Java являются ссылочными типами, как классы, но они могут содержать в себе только константы, сигнатуры методов, default методы (методы по умолчанию), static методы (статические методы) и вложенные типы. Тела методов могут быть только у статических методов и методов по умолчанию.
Нельзя создать экземпляр интерфейса. Интерфейс может быть только реализован каким-либо классом, либо наследоваться другим интерфейсом.
Пример интерфейса, описывающий общие методы для всех монстров:
Ключевое слово public означает, что интерфейс будет доступен из всех пакетов. Можно не указывать модификатор доступа, и тогда интерфейс будет package-private.
Ключевое слово abstract для метода означает, что у метода нет реализации, а ключевое слово abstract у всего интерфейса означает, что все методы экземпляров не имеют реализации (кроме статических методов и методов по умолчанию). Для классов abstract имеет примерно такое же действие, оно будет объяснено в статье про наследование.
Чтобы использовать интерфейс нужно объявить класс, реализующий этот интерфейс, с помощью ключевого слова implements :
Интерфейс
1. Что такое интерфейс?
Интерфейс это конструкция языка Java, в рамках которой принято описывать абстрактные публичные ( abstract public ) методы и статические константы ( final static ).
Все методы интерфейса являются public abstract и эти модификаторы тоже необязательны. Объявляемые методы не содержат тел, их объявления завершаются точкой с запятой:
Класс Robot из вышеуказанной схемы:
Если класс реализует интерфейс, но не полностью реализует определенные в нем методы, он должен быть объявлен как abstract .
Для указания того, что класс реализует несколько интерфейсов, после ключевого слова implements через запятую перечисляются нужные интерфейсы. Класс Pickup должен определить все методы реализуемых интерфейсов:
2. Внутренние интерфейсы
Интерфейсы объявленные в классах или в других интерфейсах называются внутренние или вложенные. Например, интерфейс NestedIf определен внутри класса A:
При обращении к интерфейсу NestedIf требуется указывать имя его внешнего класса — A.NestedIf :
3. Расширение интерфейсов
Интерфейс может наследоваться от другого интерфейса через ключевое слово extends . Один интерфейс, в отличие от классов, может расширять несколько интерфейсов.
4. Интерфейсы маркеры
5. Методы по умолчанию в интерфейсах
Интерфейс SomeInterface объявляет метод по умолчанию defaultMethod() с базовой реализацией:
Класс SomeInterfaceImpl1 , реализующий этот интерфейс, не переопределяет метод defaultMethod() — так можно.
А если класс SomeInterfaceImpl2 не устраивает реализация по умолчанию, он переопределяет этот метод:
6. Статические методы интерфейса
История эволюции интерфейсов в Java
Интерфейс в Java сильно эволюционировал за прошедшие годы. Давайте рассмотрим, какие изменения произошли в процессе его развития.
Оригинальные интерфейсы
Интерфейсы в Java 1.0 были достаточно простыми по сравнению с тем, какие они сейчас. Они могли содержать лишь два типа элементов: константы и публичные абстрактные методы.
Поля-константы
Интерфейсы могут содержать поля, так же как и обычные классы, но с несколькими отличиями:
Даже несмотря на то, что явно это не задано, поле MY_CONSTANT считается публичной статической финальной константой. Вы можете добавить эти модификаторы, но делать это не обязательно.
Абстрактные методы
Наиболее важными элементами интерфейса являются его методы. Методы интерфейса также отличаются от методов обычного класса:
Вложенность
Java 1.1 представила концепцию классов, которые можно размещать внутри других классов. Такие классы бывают двух видов: статические и нестатические. Интерфейсы так же могут содержать внутри себя другие интерфейсы и классы.
Даже если это не задано явно, такие интерфейсы и классы считаются публичными и статическими.
Перечисления и аннотации
В Java 5 были введены ещё два типа: Перечисления и Аннотации. Они также могут быть помещены внутрь интерфейсов.
Обобщенные типы
Java 5 ввела концепцию «дженериков» — обобщенных типов. Вкратце: «дженерики» позволяют вам использовать обобщенный тип вместо указания конкретного типа. Таким образом, вы можете писать код, который работает с различным количеством типов, не жертвуя при этом безопасностью и не предоставляя отдельную реализацию для каждого типа.
В интерфейсах, начиная с Java 5, вы можете определить обобщенный тип, а затем использовать его в качестве типа возвращаемого значения метода или в качестве типа аргумента метода.
Интерфейс Box работает независимо от того, используете ли вы его для хранения объектов типа String, Integer, List, Shoe или каких-либо других.
Статические методы
Начиная с Java 8, вы можете включать в интерфейсы статические методы. Данный подход изменил привычный для нас способ работы интерфейса. Они теперь работают совсем не так, как работали до Java 8. Первоначально все методы в интерфейсах были абстрактными. Это означало, что интерфейс предоставлял лишь сигнатуру, но не реализацию. Реализация оставалась за классами, реализующими ваш интерфейс.
При использовании статических методов в интерфейсах вам нужно также предоставить реализацию тела метода. Чтобы задействовать в интерфейсе такой метод, просто используйте ключевое слово static. Статические методы считаются публичными по умолчанию.
Наследование статических методов
В отличие от обычных статических методов, статические методы в интерфейсах не наследуются. Это означает, что если вы хотите вызвать такой метод, вы должны вызвать его напрямую из интерфейса, а не из реализующего его класса.
Это поведение очень полезно для избежания проблем при множественном наследовании. Представьте, что у вас есть класс, реализующий два интерфейса. У каждого из интерфейсов есть статический метод с одинаковым именем и сигнатурой. Какой из них должен быть использован в первую очередь?
Почему это полезно
Представьте, что у вас есть интерфейс и целый набор вспомогательных методов, которые работают с классами, реализующими этот интерфейс.
Традиционно существовал подход в использовании класса-компаньона. В дополнение к интерфейсу создавался утилитный класс с очень похожим именем, содержащий статические методы, принадлежащие интерфейсу.
Вы можете найти примеры использования данного подхода прямо в JDK: интерфейс java.util.Collection и сопутствующий ему утилитный класс java.util.Collections.
Со статическими методами в интерфейсах этот подход больше не актуален, не нужен и не рекомендован. Теперь вы можете иметь все в одном месте.
Методы по умолчанию
Методы по умолчанию похожи на статические методы тем, что для них вы также должны предоставлять тело. Чтобы объявить метод по умолчанию, просто используйте ключевое слово default.
В отличие от статических методов, методы по умолчанию наследуются классами, реализующими интерфейс. Что важно, такие классы могут при необходимости переопределять их поведение.
Хотя есть одно исключение. В интерфейсе не может быть методов по умолчанию с такой же сигнатурой, как у методов toString, equals и hashCode класса Object. Взгляните на ответ Брайана Гетца, чтобы понять обоснованность такого решения: Allow default methods to override Object’s methods.
Почему это полезно
Идея с реализацией методов прямо в интерфейсе выглядит не совсем правильной. Так почему в первую очередь была введена эта функциональность?
У интерфейсов есть одна проблема. Как только вы предоставите свой API другим людям, он навсегда «окаменеет» (его нельзя будет изменить безболезненно).
По традиции, Java очень серьезно относится к обратной совместимости. Методы по умолчанию предоставляют способ расширить существующие интерфейсы новыми методами. Наиболее важно то, что методы по умолчанию уже предоставляют определенную реализацию. Это означает, что классам, реализующим ваш интерфейс, не нужно реализовывать какие-либо новые методы. Но, если в этом будет необходимость, методы по умолчанию можно будет переопределить в любое время, если их реализация перестанет подходить. Таким образом, вкратце, вы можете предоставить новую функциональность существующим классам, реализующим ваш интерфейс, сохраняя при этом совместимость.
Конфликты
Давайте представим, что у нас есть класс, реализующий два интерфейса. У этих интерфейсов есть метод по умолчанию с одинаковыми именем и сигнатурой.
Теперь один и тот же метод по умолчанию с одной и той же сигнатурой унаследован от двух разных интерфейсов. У каждого интерфейса своя реализация этого метода.
Итак, как наш класс узнает, какую из двух различных реализаций использовать?
Он и не узнает. Приведенный выше код приведет к ошибке компиляции. Если вам требуется заставить его работать, то для этого необходимо переопределить конфликтный метод в вашем классе.
Приватные методы
С появлением Java 8 и введением методов по умолчанию и статических методов, у интерфейсов появилась возможность содержать не только сигнатуры методов, но и их реализации. При написании таких реализаций рекомендуется разделять сложные методы на более простые. Такой код легче переиспользовать, поддерживать и понимать.
Для такой цели вы бы использовали приватные методы, поскольку они могут содержать все детали реализации, которые не должны быть видимы и использованы извне.
К сожалению в Java 8 интерфейс не может содержать приватные методы. Это означает, что вы можете использовать:
Хронологический порядок
Ниже представлен хронологический перечень изменений по версиям Java: