Интерфейс не может содержать конструктора

Обновлено: 07.05.2024

Каковы отличия между абстрактным классом и интерфейсом?

Абстрактный класс — это класс, у которого не реализован один или больше методов (некоторые языки требуют такие методы помечать специальными ключевыми словами).

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

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

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

В некоторых языках (С++) специального ключевого слова для обозначения интерфейсов нет.

Можно считать, что любой интерфейс — это уже абстрактный класс, но не наоборот.

А где я писал о "pure virtual методах" ? Я писал о том, что у метода отсутствует реализация. А виртуальный он или нет - это детали реализации языка.

В с++ можно наследоваться от произвольного количества абстрактных классов. Употребление слов наследует/реализует - просто соглашение.

tl;dr: Абстрактный класс — средство разработки классов на нижнем уровне, средство для повторного использования кода; интерфейс — средство выражения семантики класса. Таким образом, это совершенно разные, мало связанные между собой понятия.

Думайте об этом по-другому.

Абстрактный класс — это «заготовка» класса: реализовано большинство методов (включая внутренние), кроме нескольких. Эти несколько нереализованных методов вполне могут быть внутренними методами класса, они лишь уточняют детали имплементации. Абстрактный класс — средство для повторного использования кода, средство, чтобы указать, какой метод обязан быть перекрыт для завершения написания класса.

Интерфейс же — это своего рода контракт: интерфейсы используются в определениях чтобы указать, что объект, который будет использован на самом деле, должен реализовывать (для входных параметров) или будет гарантированно реализовывать (для выходных параметров) набор методов и (что намного важнее!) иметь определённую семантику. Интерфейс вполне может быть и пустым, тем не менее, имплементировать интерфейс означает поддерживать данную семантику.

Абстрактные классы идеологически схожи с шаблонами C++: и те, и другие являются заготовками классов, но шаблону для получения класса нужно специфицировать шаблонные типы, а абстрактному классу — абстрактные методы.

Интерфейсы идеологически схожи с заголовочными файлами C++: они раскрывают методы и скрывают конкретную реализацию.

IЖивотное я думаю, даром инквизицию отменили. нельзя так код писать. уже хочется на русском - так на русском. Да и у автора был php.

это легкий троллинг, что кто то путает 1с и шарп. а по существу - есть один довод - переключаться долго. Хорошо, у меня капсом, а у людей по две кнопки жать нужно или человек будет смотреть и не понимать, почему переменная класса Сторож не приводиться к типу Стopoж (это для особых любителей загадка)

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

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

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

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

Конкретная фиксированная операция, которая должна быть абсолютно стабильно для всех типов кнопок.

Конкретная операция с поведением по умолчанию (т.е. операция, чье поведение подходит для многих типов кнопок, но могут быть кнопки с другим поведением).

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

Другими словами, тип Кнопки может содержать невиртуальные методы (non-virtual methods), виртуальные методы (virtual methods) и абстрактные методы (abstract methods).

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

После того, как мы определили базовый тип, пришло время определить произвольные типы. И тут начинаются вопросы. Точнее, вопросов никаких не возникает, когда у типа есть лишь один непосредственный базовый тип или все базовые типы содержат лишь декларации операций. Не проблема, унаследовать «Кнопку меню» от «Кнопки» и переопределить метод «Нажать на кнопку». Но что, если наш тип «Кнопка меню» будет отнаследован от двух типов с одной и той же виртуальной операцией? Как переопределить лишь одну, а оставить другую? А как быть клиенту нового типа и различить, какую операцию вызвать? А что если у двух базовых типов есть поле с одним именем? А что если у одного базового типа метод «Нажать кнопку» реализован, а у другого – лишь описан в виде декларации?

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

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

Интерфейс – описывает некоторое семейство типов и содержит лишь декларации операций (да, я осознанно пишу слово «декларация», а не использую слово «контракт», которое в ООП имеет вполне определенное значение).

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

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

Выделение интерфейсов в отдельную категорию полезно не только с точки зрения упрощения реализации языков программирования, но и для выделения разных подходов к моделированию. Так, например, наследование классов моделирует отношение «Является» («Кнопка меню» ЯВЛЯЕТСЯ «Кнопкой»), а базовые классы обычно содержат определенный функционал, тесно связанный с функционалом производного класса. Базовые классы не просто моделируют группу типов, но и позволяют использовать повторно существующий функционал.

Интерфейсы же, по своей природе обладают меньшей связностью (low coupling), поскольку не обладают конкретным поведением, которое может осложнить жизнь класса-наследника. Интерфейсы также могут моделировать отношение «Является» («Кнопка меню» ЯВЛЯЕТСЯ «IКнопкой»), но могут определять и менее жесткое отношение «Может выполнять роль» (CAN DO). Например, интерфейс IEquatable из BCL определяет «дополнительное» поведение, которое говорит о возможности типов сравнивать значения объектов.

1. Могут ли в языке Java у абстрактного класса быть конструкторы?

Да, в абстрактном классе в Java можно объявить и определить конструкторы. Поскольку создавать экземпляры абстрактных классов нельзя, вызвать такой конструктор можно только при формировании цепочки конструкторов, то есть при создании экземпляра конкретного класса-реализации. Но представьте, что интервьюер задаст затем вопрос: а какой смысл в конструкторе, если создать экземпляр абстрактного класса все равно нельзя? Дело в том, что его всё равно можно использовать для задания начальных значений общих переменных, объявленных в абстрактном классе и используемых различными реализациями. Даже если вы не объявили никакого конструктора, компилятор добавит в абстрактный класс конструктор по умолчанию без аргументов. Без него ваш подкласс не скомпилируется, поскольку первый оператор в любом конструкторе представляет собой неявный вызов super() – конструктора суперкласса по умолчанию в языке Java.

2. Могут ли абстрактные классы в языке Java реализовывать интерфейсы? Должны ли они реализовывать все методы?

Да, абстрактные классы могут реализовывать интерфейсы с помощью ключевого слова implements . Поскольку они абстрактные, то не обязаны реализовывать все методы. Наличие абстрактного базового класса и интерфейса для объявления типа является рекомендуемой практикой. Пример — интерфейс java.util.List и соответствующий абстрактный класс java.util.AbstractList . Поскольку AbstractList реализует все общие методы, то конкретные реализации (например, LinkedList и ArrayList ) не должны реализовать все методы, как в случае, если бы они реализовали интерфейс List напрямую. Это решение сочетает преимущество использования интерфейса для объявления типа и гибкость абстрактного класса для реализации всего общего поведения в одном месте. В книге Джошуа Блоха «Java. Эффективное программирование» есть отличная глава на тему использования интерфейсов и абстрактных классов в Java, для лучшего понимания имеет смысл её изучить.

3. Может ли абстрактный класс быть final?

Нет, не может. Ключевое слово final означает, что класс на вершине иерархии, и у него не может быть наследников. А абстрактный класс без наследников — это сферический конь в вакууме, так как нельзя создать экземпляр abstract class . Таким образом, если класс одновременно abstract и final , то у него нет наследников и нельзя создать его экземпляр. Компилятор Java выдаст ошибку, если сделать класс одновременно abstract и final .

4. Могут ли у абстрактного класса в языке Java быть статические методы?

Да, абстрактные классы могут объявлять и определять статические методы. Только необходимо следовать общим принципам создания статических методов в Java, поскольку они нежелательны при объектно-ориентированном проектировании, ведь переопределение статических методов в Java невозможно. Статические методы в абстрактном классе – явление очень редкое, но, если на это есть уважительные причины, вам ничего не помешает их использовать.

5. Можно ли создать экземпляр абстрактного класса?

Нет, этого делать нельзя. Суть абстрактного класса заключается в том, что он не завершён, и его нужно завершить в классах-наследниках. То есть этот класс не готов к использованию. В нём, например, может отсутствовать реализация каких-то методов. Раз класс не готов к использованию, то нельзя создавать его объект. А вот экземпляры наследников абстрактного класса создавать можно. Компилятор Java выдаст ошибку, если программа попытается создать экземпляр абстрактного класса.

6. Обязательно ли в абстрактном классе должны быть абстрактные методы?

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

7. Каковы различия между абстрактным классом и интерфейсом в Java?

  • Интерфейс описывает только поведение (методы) объекта, а вот состояний (полей) у него нет (кроме public static final ), в то время как у абстрактного класса они могут быть.
  • Абстрактный класс наследуется (extends), а интерфейс — реализуется (implements). Мы можем наследовать только один класс, а реализовать интерфейсов — сколько угодно. Интерфейс может наследовать (extends) другой интерфейс/интерфейсы.
  • Абстрактные классы используются, когда есть отношение "is-a", то есть класс-наследник расширяет базовый абстрактный класс, а интерфейсы могут быть реализованы разными классами, вовсе не связанными друг с другом.

8. Когда имеет смысл предпочесть абстрактный класс интерфейсу и наоборот?

  • Вы хотите поделиться кодом между несколькими тесно связанными классами.
  • Вы ожидаете, что классы, которые расширяют ваш абстрактный класс, имеют много общих методов или полей, или требуют других модификаторов доступа, кроме public (например, protected и private ).
  • Вы хотите объявить нестатические или не-final поля. Это позволяет вам определять методы, которые могут получить доступ и изменить состояние объекта, которому они принадлежат.
  • Вы ожидаете, что несвязанные классы будут реализовывать ваш интерфейс. Например, интерфейсы Comparable и Cloneable реализуются многими несвязанными классами.
  • Вы хотите определить поведение конкретного типа данных, но вам не важно, кто его реализует.
  • Вы хотите использовать множественное наследование типа.

9. Что такое абстрактный метод в языке Java?

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

Проектирование Классов и Интерфейсов (Перевод статьи) - 1

Независимо от того, какой язык программирования вы используете (и Java здесь не исключение), следование правильным принципам проектирования - является ключевым фактором к написанию чистого, понятного и поддающегося проверке кода; а также создавать его долгоживущим, легко поддерживающим решения проблем. В этой части урока мы собираемся обсудить фундаментальные строительные блоки, которые предоставляет язык Java, и ввести пару принципов проектирования, стремясь помочь вам сделать лучшие дизайн-решений. Точнее, мы собираемся обсудить интерфейсы и интерфейсы с использованием методов по умолчанию (новая функция Java 8), абстрактные и окончательные (final) классы, неизменяемые классы, наследование, композицию и пересмотреть правила видимости (или доступности), которых мы кратко коснулись в 1 части урока «How to create and destroy objects».

2. ИНТЕРФЕЙСЫ

В объектно-ориентированном программировании, понятие интерфейсов формирует основы развития контрактов (прим. переводчика: "Контракт" в ООП — набор четко определенных условий, регулирующих отношения между классом-сервером и его клиентами). В двух словах, интерфейсы определяют набор методов (контрактов) и каждый класс, который требует поддержки этого специфичного интерфейса, должен обеспечить реализацию этих методов: довольно простая, но мощная идея. Многие языки программирования имеют интерфейсы в той или иной форме, но Java в особенности обеспечивает поддержку языка для этого. Давайте взглянем на простое интерфейсное определение в Java. Во фрагменте выше, интерфейс, который мы назвали SimpleInterface , объявляет только один метод с именем performAction . Главные отличия интерфейсов по отношению к классам — то, что интерфейсы очерчивают, какой должен быть контакт (объявляют метод), но не обеспечивают их реализацию. Однако, интерфейсы в Java могут быть сложнее: они могут включать в себя вложенные интерфейсы, классы, подсчеты, аннотации и константы. Например: В этом более сложном примере, есть несколько ограничений, которые интерфейсы безоговорочно налагают относительно вложенных конструкций и объявлений метода, и к выполнению этого принуждает компилятор Java. Прежде всего, даже если это не было объявлено явно, каждое объявление метода в интерфейсе является публичным (public) (и может быть только публичным). Таким образом следующие объявления методов эквивалентны: Стоит упомянуть, что каждый отдельный метод в интерфейсе неявно объявлен абстрактным и даже эти объявления метода эквивалентны: Что касается объявленных полей констант, дополнительно к тому что они являются публичными, они также неявно статические и помечены, как final. Поэтому следующие объявления также эквивалентны: И, наконец, вложенные классы, интерфейсы или подсчеты, кроме того, что являются публичными, также неявно объявлены как static. К примеру, данные объявления также эквивалентны: Стиль, который вы выберете — это ваше личное предпочтение, однако знание этих простых свойств интерфейсов может спасти вас от ненужного ввода.

3. Интерфейс-маркер

Интерфейс маркеры — это особый вид интерфейса, у которого нет методов или других вложенных конструкций. Как это определяет библиотека Java: Интерфейс-маркеры — не контракты по сути, но отчасти полезная техника, чтобы «прикрепить" или «связать" некоторую специфическую черту с классом. К примеру, относительно Cloneable, класс помечен как доступный для клонирования, однако, способ, которым это можно или следует реализовать, — не является частью интерфейса. Еще один очень известный и широко используемый пример интерфейс-маркера — это Serializable : Этот интерфейс помечает класс, как пригодный для преобразования в последовательную форму (сериализацию) и десериализацию (deserialization), и снова, это не указывает способ, как это можно или следует реализовывать. Интерфейс-маркеры занимают свое место в объектно-ориентированном программировании, хотя они не удовлетворяют главную цель интерфейса, чтобы быть контрактом.

4. ФУНКЦИОНАЛЬНЫЕ ИНТЕРФЕЙСЫ, МЕТОДЫ ПО УМОЛЧАНИЮ И СТАТИЧЕСКИЕ МЕТОДЫ

С выпусков Java 8, интерфейсы получили новые очень интересные возможности: статические методы, методы по умолчанию и автоматическое преобразование из лямбд (функциональные интерфейсы). В разделе интерфейсы, мы подчеркивали тот факт, что интерфейсы в Java могут только объявлять методы, но не обеспечивают их реализацию. С методом по умолчанию, все иначе: интерфейс может отметить метод ключевым словом default и обеспечить реализацию для него. Например: Будучи на уровне экземпляра, методы по умолчанию могли быть переопределены каждой реализацией интерфейса, но теперь интерфейсы также могут включать статические методы, например: package com.javacodegeeks.advanced.design; Можно сказать, что предоставление реализации в интерфейсе наносит поражение целому замыслу контрактного программирования . Но есть много причин, почему эти функции были введены в язык Java и независимо от того, насколько они полезны или сбивающие с толку, они есть там для вас и вашего пользования. Функциональные интерфейсы это совсем другая история и они опробованы, как очень полезные дополнения к языку. В основном, функциональный интерфейс — это интерфейс всего лишь с одним абстрактным методом, объявленным в нем. Runnable интерфейс из стандартной библиотеки — это очень хороший пример этой концепции. Компилятор Java по-разному обрабатывает функциональные интерфейсы и может превратить лямбда-функцию в реализацию функционального интерфейса, где это имеет смысл. Давайте рассмотрим следующее описание функции: Для вызова этой функции в Java 7 и ниже должна предоставляться реализация интерфейса Runnable (например используя анонимные классы), но в Java 8 достаточно передать реализацию метода run() используя синтаксис лямбды: Кроме того, аннотация @FunctionalInterface (аннотации будут раскрыты в деталях в 5 части учебника) намекает, что компилятор может проверить, содержит ли интерфейс только один абстрактный метод, поэтому любые изменения, внесенные в интерфейсе в будущем не будет нарушать это предположение.

5. АБСТРАКТНЫЕ КЛАССЫ

Еще одна интересная концепция, поддерживаемая языком Java, — понятие абстрактных классов. Абстрактные классы отчасти похожи на интерфейсы в Java 7 и очень близки интерфейсу с методом по умолчанию в Java 8. В отличие от обычных классов, нельзя создавать экземпляры абстрактного классы, но он может быть подклассом (обратитесь к разделу "Наследование" для получения более подробной информации). Что еще более важно, абстрактные классы могут содержать абстрактные методы: особый вид методов без реализации, так же, как и интерфейс. Например: В этом примере, класс SimpleAbstractClass объявлен как абстрактный и содержит один объявленный абстрактный метод. Абстрактные классы очень полезны, большинство или даже некоторые части деталей реализации могут совместно использоваться с многими подклассами. Как бы там ни было, они по-прежнему оставляют дверь приоткрытой и позволяют настроить поведение присущее каждому из подкласса с помощью абстрактных методов. Стоит упомянуть, в отличие от интерфейсов, которые могут содержать только публичные объявления, абстрактные классы могут использовать всю мощь правил доступности, чтоб управлять видимостью абстрактного метода.

6. НЕИЗМЕНЯЕМЫЕ КЛАССЫ

Неизменяемость становится все более и более важной в разработке программного обеспечения в наше время. Подъем многоядерных систем вызвало много вопросов, связанных с совместным использованием данных и параллелизмом. Но одна проблема определенно возникла: небольшое (или даже отсутствие) изменяемого состояния приводит к лучшей расширяемости (масштабируемости) и более простому рассуждению о системе. К сожалению, язык Java не обеспечивает достойную поддержку классовой неизменности. Однако, пользуясь комбинацией техник, становится возможно проектировать классы, которые неизменны. Прежде всего, все поля класса должны быть окончательные (помечены как final). Это хорошее начало, но не дает гарантий. Во вторых, следите за правильной инициализацией: если поле является ссылкой на коллекцию или массив, не назначайте те поля непосредственно из аргументов конструктора, вместе этого делайте копии. Это будет гарантировать, что состоянии коллекции или массива не будет изменено за пределами. И наконец, обеспечение надлежащего доступа (гетеры). Для коллекций, неизменяемый вид должен быть предоставлен в виде обертки Collections.unmodifiableXxx : С массивами единственный способ обеспечить настоящую неизменяемость – это предоставить копию вместо возвращения ссылки на массив. Это может быть неприемлемо с практической точки зрения, так как это очень зависит от размера массива и может возложить огромное давление на сборщика мусора. Даже этот маленький пример дает хорошую идею, что неизменяемость еще не гражданин первого класса в Java. Все может быть усложнено, если неизменяемый класс имеет поле, ссылающийся на объект другого класса. Те классы должны быть также неизменны, однако нет никакого способа это обеспечить. Есть несколько достойных анализаторов исходного кода в Java, как FindBugs и PMD, которые могут существенно помочь, проверяя ваш код и указывая на общие недостатки программирования Java. Эти инструменты - большие друзья любого разработчика Java.

7. АНОНИМНЫЕ КЛАССЫ

В предварительной Java 8 era, анонимные классы были единственным способом обеспечить оперативное определение классов и немедленное создание экземпляра. Целью анонимных классов было уменьшить шаблон и обеспечить краткий и легкий путь представления классов как запись. Давайте взглянем на типичный старомодный путь породить новый поток в Java: В этом примере реализация Runnable interface предоставляется сразу как анонимный класс. Хотя есть некоторые ограничения, связанные с анонимными классами, основные недостатки их использования — весьма подробный синтаксис конструкций, которым обязывает Java как язык. Даже просто анонимный класс, который ничего не делает, требует по меньшей мере 5 линий кода каждый раз при записи. К счастью, с Java 8, лямбдой и функциональными интерфейсами все эти стереотипы скоро уйдут, наконец написание кода Java будет выглядеть по настоящему кратко.

8. ВИДИМОСТЬ

Проектирование Классов и Интерфейсов (Перевод статьи) - 2

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

9. НАСЛЕДОВАНИЕ

Наследование — одно из ключевых понятий объектно-ориентированного программирования, выступающее в качестве основы построения класса связей. В сочетании с видимостью и правилами доступности, наследование позволяет проектировать классы иерархии, с возможностью расширения и поддерживания. На понятийном уровне, наследование в Java реализуется с помощью создание подклассов и ключевого слова extends, вместе с родительским классом. Подкласс наследует все публичные и защищенные элементы родительского класса. Кроме того, подкласс наследует package-private элементы родительского класса, если оба (подкласс и класс) находятся в одном пакете. При этом, очень важно, независимо от того, что вы пытаетесь спроектировать, придерживаться минимального набора метода, которые класс выставляет публично или для его подклассов. Например, давай те рассмотрим класс Parent и его подкласс Child , чтобы продемонстрировать разницу в уровнях видимости и их эффекты. Наследование — очень большая тема сама по себе, с большим количеством тонких деталей, характерных для Java. Однако, есть несколько правил, которым легко следовать, и которые могут очень помочь сохранить краткость в классовой иерархии. В Java, каждый подкласс может переопределять любые унаследованные методы его родителя, если он не был объявлен как окончательный (final). Однако, нет специального синтаксиса или ключевого слова, чтобы пометить метод, как переопределенный, что может привести к путанице. Вот почему была введена аннотация @Override: всякий раз , когда ваша цель – переопределить наследуемый метод, пожалуйста, используйте аннотацию @Override, чтобы кратко обозначить это. Другая дилемма, с которой Java разработчики постоянно сталкиваются в проектирование — это построение классов иерархии (с конкретными или абстрактными классами) в сравнении с реализацией интерфейсов. Настоятельно рекомендуем отдавать предпочтение интерфейсам по отношению к классам или абстрактным классам, где это возможно. Интерфейсы более легкие, их проще тестировать и поддерживать, плюс ко всему, они минимизируют побочные эффект изменений реализации. Многие продвинутые техники программирования, такие как создание прокси (proxy) классов в стандартной библиотеке Java, в большей степени полагаются на интерфейсы.

10. МНОЖЕСТВЕННОЕ НАСЛЕДОВАНИЕ

В отличие от С++ и некоторых других языков, Java не поддерживает множественное наследование: в Java каждый класс может иметь только одного прямого родителя (с классом Object в вершине иерархии). Однако, класс может реализовывать несколько интерфейсов, и, таким образом, стекование (stacking) интерфейсов — единственный способ достигнуть (или имитировать) множественное наследование в Java. Реализация нескольких интерфейсов на самом деле довольно мощная, но часто необходимость снова и снова использовать реализацию приводит к глубокой классовой иерархии, как способ преодолеть отсутствие поддержки множественного наследования в Java. И так далее.. Недавний выпуск Java 8 несколько решает проблему с внедрением методов по умолчанию. Из-за методов по умолчанию, интерфейсы фактические стали предоставлять не только контракт, но и реализацию. Следовательно, классы, которые реализуют эте интерфейсы, также автоматически унаследуют эти реализованные методы. Например: Имейте в виду, что множественное наследование очень мощный, но и в тоже время опасный инструмент. Хорошо известную проблему «Ромб смерти» часто называют основным дефектом реализации множественного наследования, поэтому разработчики вынуждены проектировать классы иерархии весьма тщательно. К сожалению, интерфейсы Java 8 с методами по умолчанию также становится жертвой этих дефектов. Например, следующий фрагмент кода не удастся скомпилировать: На данный момент, справедливо сказать, что Java как язык всегда пытался избежать угловых случаем объектно-ориентированного программирования, но, так как язык развивается, некоторые из тех случаем стали внезапно появляется.

11. НАСЛЕДОВАНИЕ И КОМПОЗИЦИЯ

  • Проектирование более гибкое;
  • Модель более стабильная, так как изменения не распространяются через классовую иерархию;
  • Класс и его композиция слабо связаны по сравнению с композицией, которая плотно связывает родителя и его подкласс.
  • Логический ход мысли в классе проще, так как все его зависимости включены в нем же, в одном месте.

12. ИНКАПСУЛЯЦИЯ.

Понятие инкапсуляции в объектно-ориентированном программировании заключается в скрытие всех деталей реализации (как режим работы, внутренние методы и т.д.) от внешнего мира. Преимущества от инкапсуляции в удобстве сопровождения и легкости изменений. Внутренняя реализация класса скрывается, работа с данными класса происходит исключительно через публичные методы класса (реальная проблема, если вы разрабатываете библиотеку или фреймфорки (структуры), использованную многими людьми). Инкапсуляция в Java достигается с помощью правил видимости и доступности. В Java, считается, что лучше всего никогда не выставлять поля напрямую, только посредством гетеров (getters) и сетеров (setters) (если поля не помечены как окончательные (final)). Например: Этот пример напоминает то, что называется JavaBeans в языке Java: стандартные классы Java, написаны соответственно набору соглашений, один из которых дает доступ к полям только с помощью гетер и сеттер методов. Как мы уже подчеркивали в разделе наследования, пожалуйста, всегда придерживайте минимальному контракту публичности в классе, используя принципы инкапсуляции. Все, что не должно быть публичным - должно стать приватным (или protected/ package private, зависит от проблемы, что вы решаете). В долгосрочной перспективе это вам окупится, давая вам свободу в проектировании без внесения критических изменений (или, по крайней мере, минимизируют их).

13. ОКОНЧАТЕЛЬНЫЕ КЛАССЫ И МЕТОДЫ

В Java, есть способ предотвратить возможность класса стать подклассом от другого класса: другой класс должен быть объявлен как окончательный (final). Это же ключевое слово final в объявление метода предотвращает возможность переопределения метода в подклассах. Нет общих правил, чтобы решить должен класс или методы быть окончательными или нет. Окончательные классы и методы ограничивают расширяемость и очень сложно думать наперед, должен или не должен класс быть унаследованным, или должен или не должен метод быть переопределен в будущем. Это особенно важно для разработчиков библиотеки, поскольку решения проектирования подобны этому могли бы существенно ограничить применимость библиотеки. Стандартная библиотека Java имеет несколько примеров окончательный классов, причем наиболее известный - это класс String. На ранней стадии, было принято данное решение, чтобы предотвратить любые попытки разработчиков появиться с собственным, «лучшим» решением реализации string.

14. ЧТО ДАЛЬШЕ

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

I know it's not possible to define a constructor in an interface. But I'm wondering why, because I think it could be very useful.

So you could be sure that some fields in a class are defined for every implementation of this interface.

For example consider the following message class:

If a define an interface for this class so that I can have more classes which implement the message interface, I can only define the send method and not the constructor. So how can I ensure that every implementation of this class really has an receiver set? If I use a method like setReceiver(String receiver) I can't be sure that this method is really called. In the constructor I could ensure it.

You say "In the constructor I could ensure [every implementation of this class really has an receiver set]." But no, you couldn't possibly do that. Provided it was possible to define such a constructor, the parameter would only be a strong hint to your implementors – but they could chose to simply ignore it if they wanted to.

If you want to ensure a class has a certain constructor B for some functional purpose P , consider requiring that P receives the B as a parameter somewhere. That way, depending on how you declare it, ( Supplier , BiFunction . ), you can guarantee behavior. For an example take a look at this solution.

11 Answers 11

Taking some of the things you have described:

"So you could be sure that some fields in a class are defined for every implementation of this interface."

"If a define a Interface for this class so that I can have more classes which implement the message interface, I can only define the send method and not the constructor"

. these requirements are exactly what abstract classes are for.

but note that the use case @Sebi describes (calling overloaded methods from parent constructors) is a bad idea as explained in my answer.

Matt, that's clearly true, but abstract classes suffer from the single-inheritance limitation, which leads people to look at other ways of specifying hierarchies.

This is true and may solve Sebi's immediate problem. But one reason for using interfaces in Java is because you cannot have multiple inheritance. In a case where I cannot make my "thing" an abstract class because I need to inherit from something else, the problem remains. Not that I claim to have a solution.

@CPerkins while this is true, I am not suggesting that simply using an abstract class will solve Sebi's use case. If anything, it's best to declare a Message interface which defines the send() method, and if Sebi wishes to provide a "base" class for implementations of the Message interface, then provide an AbstractMessage as well. Abstract classes shouldn't take the place of interfaces, was never attempting to suggest so.

Understood, Matt. I wasn't arguing with you, more pointing out that it's not a complete replacement for what the op wants.

A problem that you get when you allow constructors in interfaces comes from the possibility to implement several interfaces at the same time. When a class implements several interfaces that define different constructors, the class would have to implement several constructors, each one satisfying only one interface, but not the others. It will be impossible to construct an object that calls each of these constructors.

The most useful meaning for a "constructor in an interface", if allowed, would be if new Set() could be interpreted to mean "Give me something I can use as a Set "; if the author of Set intended HashSet to be the go-to implementation for things that didn't have a particular need for something else, the interface could then define new Set() could be considered synonymous with new HashSet() . For a class to implement multiple interfaces would not pose any problem, since new InterfaceName() would simply construct a class designated by the interface.

Counter-argument: your A(String,List) constructor could be the designated constructor, and A(String) and A(List) could be secondary ones that call it. Your code isn't a counter-example, just a poor one.

Why would you call all the constructors in an implementation?! Yes, if it implements more Interfaces with ctor, one with a String and one with an int, it needs that two ctors - but either or can be used. If thats not applicable, the class simply doesn't implement both Interface. So what!? (there are other reasons for not having ctor in Interfaces though ).

@kai No, it needs to call both interface constructors when constructing an instance. In other words: In my example, the instance has both a name and a list, so each instance needs to instantiate both the name and the list.

An interface defines a contract for an API, that is a set of methods that both implementer and user of the API agree upon. An interface does not have an instanced implementation, hence no constructor.

The use case you describe is akin to an abstract class in which the constructor calls a method of an abstract method which is implemented in an child class.

The inherent problem here is that while the base constructor is being executed, the child object is not constructed yet, and therfore in an unpredictable state.

To summarize: is it asking for trouble when you call overloaded methods from parent constructors, to quote mindprod:

In general you must avoid calling any non-final methods in a constructor. The problem is that instance initialisers / variable initialisation in the derived class is performed after the constructor of the base class.

A work around you can try is defining a getInstance() method in your interface so the implementer is aware of what parameters need to be handled. It isn't as solid as an abstract class, but it allows more flexibility as being an interface.

However this workaround does require you to use the getInstance() to instantiate all objects of this interface.

There is only static fields in interface that dosen't need to initialized during object creation in subclass and the method of interface has to provide actual implementation in subclass .So there is no need of constructor in interface.

Second reason-during the object creation of subclass, the parent constructor is called .But if there will be more than one interface implemented then a conflict will occur during call of interface constructor as to which interface's constructor will call first

If you want to make sure that every implementation of the interface contains specific field, you simply need to add to your interface the getter for that field:

  • it won't break encapsulation
  • it will let know to everyone who use your interface that the Receiver object has to be passed to the class in some way (either by constructor or by setter)

Dependencies that are not referenced in an interfaces methods should be regarded as implementation details, not something that the interface enforces. Of course there can be exceptions, but as a rule, you should define your interface as what the behavior is expected to be. Internal state of a given implementation shouldn't be a design concern of the interface.

This is because interfaces do not allow to define the method body in it.but we should have to define the constructor in the same class as interfaces have by default abstract modifier for all the methods to define. That's why we can not define constructor in the interfaces.

See this question for the why (taken from the comments).

If you really need to do something like this, you may want an abstract base class rather than an interface.

Here´s an example using this Technic. In this specifik example the code is making a call to Firebase using a mock MyCompletionListener that is an interface masked as an abstract class, an interface with a constructor

Generally constructors are for initializing non-static members of particular class with respect to object.

There is no object creation for interface as there is only declared methods but not defined methods. Why we can’t create object to declared methods is-object creation is nothing but allocating some memory (in heap memory) for non-static members.

JVM will create memory for members which are fully developed and ready to use.Based on those members , JVM calculates how much of memory required for them and creates memory.

Incase of declared methods, JVM is unable to calculate the how much memory will required to these declared methods as the implementation will be in future which is not done by this time. so object creation is not possible for interface.

without object creation, there is no chance to initialize non-static members through a constructor.That is why constructor is not allowed inside a interface.(as there is no use of constructor inside a interface)

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

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

Определение IEquatable не предоставляет реализацию для метода Equals . Класс или структура может реализовывать несколько интерфейсов, но класс может наследовать только от одного класса.

Дополнительные сведения об абстрактных классах см. в разделе Абстрактные и запечатанные классы и члены классов.

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

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

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

Свойства и индексаторы класса могут определять дополнительные методы доступа для свойства или индексатора, определенного в интерфейсе. Например, интерфейс может объявлять свойство, имеющее акцессор get. Класс, реализующий этот интерфейс, может объявлять это же свойство с обоими акцессорами ( get и set). Однако если свойство или индексатор использует явную реализацию, методы доступа должны совпадать. Дополнительные сведения о явной реализации см. в статьях Явная реализация интерфейса и Свойства интерфейса.

Интерфейс может наследовать от одного или нескольких интерфейсов. Производный интерфейс наследует члены от своих базовых интерфейсов. Класс, реализующий производный интерфейс, должен реализовывать все члены в нем, включая все члены базовых интерфейсов производного интерфейса. Этот класс может быть неявно преобразован в производный интерфейс или любой из его базовых интерфейсов. Класс может включать интерфейс несколько раз через наследуемые базовые классы или через интерфейсы, которые наследуются другими интерфейсами. Однако класс может предоставить реализацию интерфейса только однократно и только если класс объявляет интерфейс как часть определения класса ( class ClassName : InterfaceName ). Если интерфейс наследуется, поскольку наследуется базовый класс, реализующий этот интерфейс, то базовый класс предоставляет реализацию членов этого интерфейса. Но производный класс может повторно реализовать любые члены виртуального интерфейса и не использовать наследованную реализацию. Когда интерфейсы объявляют реализацию метода по умолчанию, любой класс, реализующий этот интерфейс, наследует эту реализацию (необходимо привести экземпляр класса к типу интерфейса для доступа к реализации по умолчанию в члене интерфейса).

Базовый класс также может реализовывать члены интерфейса с помощью виртуальных членов. В таком случае производный класс может изменять поведение интерфейса путем переопределения виртуальных членов. Дополнительные сведения о виртуальных членах см. в статье Полиморфизм.

Читайте также: