Абстрактные классы и интерфейсы для чего и что всё же? [дубликат]
1. Абстрактный класс
По сути это самый обычный класс. Только что мы не можем создать объект такого класса. Как я читал, это класс, который говорит об объекте абстрактно. То есть мы можем в классе реализовать методы, но не можем создать его объект.
2. Интерфейсы
Нельзя реализовывать методы, поля только статичные и константы. И несмотря на то, что нельзя реализовывать методы, я нахожу пример с метанита, где реализован метод по умолчанию:
interface IMovable < void Move() < Console.WriteLine("Walking"); >// реализация свойства по умолчанию // свойство только для чтения int MaxSpeed < get < return 0; >> >
и ещё вопрос. Интерфейс и абстрактный класс они же получается ссылочные типы?
Отслеживать
задан 14 дек 2021 в 11:00
Michael Clifford Michael Clifford
331 1 1 серебряный знак 11 11 бронзовых знаков
Вот тут уже ответ есть
14 дек 2021 в 11:12
1 ответ 1
Сортировка: Сброс на вариант по умолчанию
Вопрос по сути про ООП наследование и полиморфизм.
Члены класса могут быть обычными, абстрактными и виртуальными.
- Обычный член не может быть переопределен в наследниках, а только заменен.
- Виртуальный член может быть переопределен, а может и нет (полиморфизм)
- Абстрактный член должен быть переопределен
Абстрактный класс «должен быть переопределен», то есть унаследован, так как может содержать нереализованные (абстрактные) члены.
Экземпляр абстрактного класса невозможно создать. Класс — это по сути шаблон для создания объекта. Абстрактный класс — частичный шаблон, то есть самого по себе абстрактного класса недостаточно для создания объекта, и недостающие части реализуются в неследнике.
Интерфейс — частный случай абстрактного класса, где все члены по умолчанию абстрактные и не приватные, при этом полностью отсутствует и не может быть определен конструктор.
Начиная с C# 8.0 (.NET Core 3.1) появились так называемые Default Interface Methods (DIM), где стало допустимо вставлять в методы изначальную реализацию. Эти методы технически считаются виртуальными в обязательном порядке. Сами по себе DIM — сомнительная затея, нарушают целостность структуры приложения, дают возможность внедрения кода в абстракции. Это как бы удобно, когда у вас есть проект-монстр размером с Visual Studio, но кроме этого плюсов от данного нововведения нет, только всякие баги, с которыми даже Microsoft не знает, что делать. Не используйте DIM без особой надобности.
При реализации интерфейса обязательно переопределение всех его членов кроме DIM. При этом при переопределении виртуальных членов абстрактного класса нужно использовать ключевое слово override , а при переопределении виртуальных членов интерфейса — не нужно, так как оно само по себе подразумевается.
Основное преимущество интерфейса перед абстрактным классом — множественное наследование. То есть не наследование, а «реализация», один класс может реализовать несколько интерфейсов одновременно. В случае с абстрактным классом класс-наследник может наследовать только один радительский класс.
Говоря простым языком, да, интерфейс — это ссылочный тип. Но интерфейс — «обертка» над классом, асбтракция, он не может быть сам по себе ссылочным или значимым типом, вы не можете создать объект типа «интерфейс». Он — сама ссылка, а внутри объект — конкретный экземпляр класса. Все что вы можете, это создать обычный экземпляр и сделать апкаст типа его ссылки до реализованного этим классом интерфейса.
Interface vs Abstract class [дубликат]
Привет, сообщество. В чем различие абстрактного класса от интерфейса в C#? Ведь можно сказать, что абстрактный класс, в котором все методы не имеют реализации и помечены как абстрактные, в каком-то роде является интерфейсом. Да, есть существенное различие — интерфейс описывает поведение объекта, т.е. его надо полностью реализовать, в отличие от абстрактного класса в котором, не реализовав абстрактные методы, наследник тоже станем абстрактным классом. Я так понимаю, что интерфейс описывает общее поведение без реализации (для каждого класса своя), а абстрактный класс тогда что делает?
Отслеживать
задан 11 янв 2017 в 21:25
Vadim Prokopchuk Vadim Prokopchuk
2,722 2 2 золотых знака 17 17 серебряных знаков 41 41 бронзовый знак
2 ответа 2
Сортировка: Сброс на вариант по умолчанию
Интерфейсы пустотелые, поэтому внутри инстанса объекта ничего не двигается и можно спокойно приводить ссылку к любому предку. А класс может содержать поля. Если унаследоваться от двух классов с полями (если бы так было можно), то приведение ко второму требовало бы сдвига ссылки.
А вообще, где-то был вопрос с ответом, охватывающий ещё и семантическую разницу между ними. Сейчас поищу. Нашёл. Проголосовал за закрытие этого как дубликата.
Отслеживать
ответ дан 11 янв 2017 в 21:46
122k 24 24 золотых знака 125 125 серебряных знаков 299 299 бронзовых знаков
Спасибо. По ссылке нашел все ответы
12 янв 2017 в 19:01
В двух словах можно сказать, что абстрактные классы служат для построения иерархии классов с общими свойствами и поведением а интерфейсы определяют общие свойства и поведение для объектов различных классов, не связанных в иерархию.
Отслеживать
ответ дан 11 янв 2017 в 21:37
Vlad from Moscow Vlad from Moscow
44.8k 3 3 золотых знака 38 38 серебряных знаков 89 89 бронзовых знаков
- c#
- ооп
-
Важное на Мете
Связанные
Похожие
Дизайн сайта / логотип © 2023 Stack Exchange Inc; пользовательские материалы лицензированы в соответствии с CC BY-SA . rev 2023.11.21.1314
Нажимая «Принять все файлы cookie» вы соглашаетесь, что Stack Exchange может хранить файлы cookie на вашем устройстве и раскрывать информацию в соответствии с нашей Политикой в отношении файлов cookie.
Разница между абстрактным классом и интерфейсом
В программировании используются понятия абстрактного класса и интерфейса. В чем заключаются их особенности?
В чем специфика абстрактного класса в программировании?
В числе основных признаков абстрактного класса — наличие реализации. При этом в нем может присутствовать один или несколько ее методов. В абстрактном классе есть свойства, имеющие модификаторы доступа. Кроме того, рассматриваемая категория данных предполагает:
- применение метода extend;
- описание конструктора;
- использование разных типов данных — в частности, статических, или же констант;
- независимость реализации наследуемого абстрактного класса от родительского.
Что такое интерфейс?
Интерфейс, в свою очередь, не содержит реализации. В нем присутствует описание в чистом виде — как совокупность методов, подлежащих реализации в конкретном классе, посредством которого осуществляется имплементация того или иного интерфейса.
Рассматриваемая категория данных также предполагает:
- наличие только тех констант, которые классифицируются как общедоступные;
- применение метода implement;
- воспроизведение всех методов интерфейса в процессе его реализации.
Отметим, что в интерфейсе не осуществляется описание конструктора.
Сравнение
Очевидно, что есть не одно отличие абстрактного класса от интерфейса. Разница между ними прослеживается в самых разных аспектах. Наиболее наглядно их можно увидеть при сопоставлении абстрактного класса и интерфейса по следующим основным критериям:
- наследование;
- реализация;
- использование конструктора.
Отобразить то, в чем разница между абстрактным классом и интерфейсом прослеживается в соотнесении с отмеченными критериями, нам поможет небольшая таблица.
Таблица
| Абстрактный класс | Интерфейс |
| Только один абстрактный класс наследуется иным классом | Несколько интерфейсов может быть реализовано каким-либо классом |
| Допускает реализацию методов | Предполагает исключительно описание методов |
| Допускает создание конструктора | Не предполагает создания конструкторов |
Похожие статьи
/rating_on.png)
/rating_on.png)
/rating_on.png)
(2 оценок, среднее: 5,00 из 5)
Абстрактные классы и интерфейсы в Java
Абстрактные классы и интерфейсы встречаются повсюду как в Java-приложениях, так и в самом Java Development Kit (JDK). Каждый из них служит своей цели:
- Интерфейс — это контракт, который должен быть реализован конкретным классом.
- Абстрактный класс похож на обычный, но отличается тем, что может содержать абстрактные методы — методы без реализации, и нельзя создать экземпляр абстрактного класса.
Многие разработчики не видят разницы между интерфейсами и абстрактными классами, но на самом деле между ними есть весьма существенное различие.
Интерфейсы
Интерфейс — это контракт, который реализуется в некотором классе. У интерфейса не может быть состояния, поэтому в нем нельзя использовать изменяемые поля экземпляра. В интерфейсе могут быть только неизменяемые final-поля.
Когда использовать интерфейсы
Интерфейсы очень полезны для уменьшения связанности (coupling) кода и реализации полиморфизма. Для примера давайте взглянем на интерфейс List из JDK:
public interface List extends Collection
Как вы, вероятно, заметили, код весьма краток и лаконичен. Здесь мы видим сигнатуры методов, которые будут реализованы в конкретном классе, реализующем этот интерфейс.
Контракт интерфейса List реализуется классами ArrayList , Vector , LinkedList и другими.
При использовании полиморфизма тип переменной объявляем как List , и присваиваем ей любую из доступных реализаций. Например:
List list = new ArrayList(); System.out.println(list.getClass()); List list = new LinkedList(); System.out.println(list.getClass());
class java.util.ArrayList class java.util.LinkedList
В этом случае в каждом классе присутствует своя реализация методов. И это отличный пример использования интерфейсов. Если вы заметили, что ряд ваших классов содержит одинаковые методы, но с разными реализациями, то стоит использовать интерфейс.
Переопределение метода интерфейса
Помните, что интерфейс — это контракт, который должен быть реализован конкретным классом. Методы интерфейса неявно абстрактны и обязаны быть реализованы в классе, реализующем этот интерфейс.
Рассмотрим следующий пример:
public class OverridingDemo < public static void main(String[] args) < Challenger challenger = new JavaChallenger(); challenger.doChallenge(); >> interface Challenger < void doChallenge(); >class JavaChallenger implements Challenger < @Override public void doChallenge() < System.out.println("Challenge done!"); >>
Результат будет следующий:
Challenge done!
Обратите внимание еще раз, что методы интерфейса неявно абстрактны и их не нужно явно объявлять как abstract.
Неизменяемые переменные
Еще одно правило, которое следует помнить, заключается в том, что интерфейс может содержать только неизменяемые переменные. Следующий код вполне рабочий:
public interface Challenger
Обратите внимание, что обе переменные неявно final и static . Это означает, что они являются константами, не зависят от экземпляра и не могут быть изменены.
При попытке изменить поля в интерфейсе Challenger , например, следующим образом:
Challenger.number = 8; Challenger.name = "Another Challenger";
будет ошибка компиляции:
Cannot assign a value to final variable 'number' Cannot assign a value to final variable 'name'
Default-методы
После появления в Java 8 методов по умолчанию, некоторые разработчики решили, что интерфейсы стали абстрактными классами. Однако это не так, поскольку у интерфейсов не может быть состояния.
У методов по умолчанию может быть реализация, а у абстрактных методов — нет. Методы по умолчанию — результат появления лямбда-выражений и Stream API, но использовать их нужно с осторожностью.
В качестве примера default-метода из JDK можно привести метод forEach() из интерфейса Iterable . Вместо копирования кода этого метода во все реализации Iterable , мы можем переиспользовать метод forEach :
default void forEach(Consumer action) < // Code implementation here…
Любая реализация Iterable может использовать метод forEach() без необходимости реализации этого нового метода.
Давайте рассмотрим пример с методом по умолчанию:
public class DefaultMethodExample < public static void main(String[] args) < Challenger challenger = new JavaChallenger(); challenger.doChallenge(); >> class JavaChallenger implements Challenger < >interface Challenger < default void doChallenge() < System.out.println("Challenger doing a challenge!"); >>
Challenger doing a challenge!
Важно отметить, что у default-метода должна быть реализация и default-метод не может быть статическим.
Абстрактные классы
У абстрактных классов может быть состояние в виде изменяемых полей экземпляра. Например:
public abstract class AbstractClassMutation < private String name = "challenger"; public static void main(String[] args) < AbstractClassMutation abstractClassMutation = new AbstractClassImpl(); abstractClassMutation.name = "mutated challenger"; System.out.println(abstractClassMutation.name); >> class AbstractClassImpl extends AbstractClassMutation
mutated challenger
Абстрактные методы в абстрактных классах
Аналогично интерфейсам в абстрактных классах могут быть абстрактные методы. Абстрактный метод — это метод без тела (без реализации). Но в отличие от интерфейсов, абстрактные методы в абстрактных классах должны быть явно объявлены как абстрактные.
public abstract class AbstractMethods
Попытка объявить метод без реализации и без ключевого слова abstract , например, следующим образом:
public abstract class AbstractMethods
приведет к ошибке компиляции:
Missing method body, or declare abstract
Когда использовать абстрактные классы
Рекомендуется использовать абстрактный класс, когда вам нужно изменяемое состояние. В качестве примера можно привести класс AbstractList из Java Collections Framework, который использует состояние.
Если хранить состояние класса не нужно, обычно лучше использовать интерфейс.
Хороший пример использования абстрактных классов — паттерн "шаблонный метод" (template method). Шаблонный метод манипулирует переменными экземпляра (полями) внутри конкретных методов.
Различия между абстрактными классами и интерфейсами
С точки зрения объектно-ориентированного программирования основное различие между интерфейсом и абстрактным классом заключается в том, что интерфейс не может иметь состояния, тогда как абстрактный класс может (в виде полей экземпляра).
Другое ключевое различие заключается в том, что классы могут реализовывать более одного интерфейса, но расширять только один абстрактный класс. Множественное наследование может привести к тупиковым ситуациям в коде, поэтому авторы Java решили этого избежать, отказавшись от него.
Еще одно различие состоит в том, что интерфейс может быть реализован классом или расширен другим интерфейсом, а класс может быть только расширен.
Также важно отметить, что лямбда-выражения могут использоваться только с функциональными интерфейсами (интерфейс только с одним методом), но не с абстрактными классами с одним абстрактным методом.
В таблице 1 обобщены различия между абстрактными классами и интерфейсами.
Таблица 1. Сравнение интерфейсов и абстрактных классов
Интерфейсы
Абстрактные классы
Могут содержать только final static поля. Интерфейс никогда не может изменять свое состояние.
Могут быть любые поля, в том числе статические, изменяемые и неизменяемые.
Класс может реализовывать несколько интерфейсов.
Класс может расширять только один абстрактный класс.
Может быть реализован с помощью ключевого слова implements.
Может расширять другой интерфейс с помощью extends.
Может быть только расширен с помощью extends.
Можно использовать только static final поля. Параметры и локальные переменные в методах.
Могут быть изменяемые поля экземпляра. Параметры и локальные переменные в методах.
В лямбда-выражениях могут использоваться только функциональные интерфейсы.
Абстрактные классы с одним абстрактным методом не могут использоваться в лямбда-выражениях.
Не может быть конструктора.
Может содержать конструктор.
Могут быть абстрактные методы.
Могут быть default и static методы (c Java 8).
Могут быть private методы с реализацией (с Java 9).
Могут быть любые методы.
Задачка
Давайте изучим основные различия между интерфейсами и абстрактными классами с помощью небольшой задачки. Вы также можете посмотреть данный материал в формате видео (англ.).
В приведенном ниже коде объявлены интерфейс, абстрактный класс и используются лямбда-выражения.
public class AbstractResidentEvilInterfaceChallenge < static int nemesisRaids = 0; public static void main(String[] args) < Zombie zombie = () ->System.out.println("Graw. " + nemesisRaids++); System.out.println("Nemesis raids: " + nemesisRaids); Nemesis nemesis = new Nemesis() < public void shoot() < shoots = 23; >>; Zombie.zombie.shoot(); zombie.shoot(); nemesis.shoot(); System.out.println("Nemesis shoots: " + nemesis.shoots + " and raids: " + nemesisRaids); > > interface Zombie < Zombie zombie = () ->System.out.println("Stars. "); void shoot(); > abstract class Nemesis implements Zombie
Как вы думаете, какой будет вывод, когда мы запустим этот код? Выберите один из следующих вариантов:
Вариант 1
Compilation error at line 4
Вариант 2
Graw. 0 Nemesis raids: 23 Stars. Nemesis shoots: 23 and raids:1
Вариант 3
Nemesis raids: 0 Stars. Graw. 0 Nemesis shoots: 23 and raids: 1
Nemesis raids: 0 Stars. Graw. 1 Nemesis shoots: 23 and raids:1
Вариант 5
Compilation error at line 6
Разбор задачи
Эта задачка демонстрирует понятия об интерфейсах, абстрактных методах и о некоторых других вещах. Давайте разберем код строка за строкой.
В первой строке main() присутствует лямбда-выражение для интерфейса Zombie. Обратите внимание, что в этой лямбде мы инкрементируем статическое поле. Здесь также можно было использовать поле экземпляра, но не локальную переменную, объявленную вне лямбда-выражения. То есть код компилируется без ошибок. Также обратите внимание, что это лямбда-выражение еще не выполняется, оно только объявлено, и поле nemesisRaids не будет увеличено.
Далее мы выводим значение поля nemesisRaids , которое еще не увеличено. Следовательно, вывод будет:
Nemesis raids: 0
Еще один интересный момент заключается в том, что мы используем анонимный внутренний класс. Мы создаем не экземпляр абстрактного класса Nemesis , но экземпляр анонимного класса, расширяющего Nemesis . Также обратите внимание, что первый конкретный класс в иерархии наследования всегда будет обязан реализовать абстрактные методы.
В интерфейсе Zombie есть поле с типом интерфейса Zombie , объявленное с помощью лямбда-выражения. Поэтому, когда мы вызываем метод Zombie.zombie.shoot() , получим следующий вывод:
Stars.
В следующей строке вызывается лямбда-выражение, которое мы создали в начале. Следовательно, переменная nemesisRaids будет увеличена. Однако, поскольку мы используем оператор постинкремента, она будет увеличена только после этого выражения. Следующий вывод будет:
Graw. 0
Далее вызовем метод shoot для nemesis , который изменяет поле экземпляра shoots на 23. Обратите внимание, что как раз здесь мы видим основную разницу между интерфейсом и абстрактным классом.
Наконец, мы выводим значение nemesis.shoots и nemesisRaids .
Nemesis shoots: 23 and raids: 1
Правильный ответ — вариант 3:
Nemesis raids: 0 Stars. Graw. 0 Nemesis shoots: 23 and raids: 1
Материал подготовлен в преддверии старта специализации Java-разработчик.
Недавно в рамках специализации прошел открытый урок, на котором мы обсудили алгоритм бинарного поиска, разобрались, почему он быстрее линейного. А также познакомились с понятием «О-большое». Делимся записью этого урока.
- java
- абстрактные классы
- интерфейсы
- бинарный поиск
- Блог компании OTUS
- Программирование
- Java
