Интерфейсы как тип абстрактного класса c
Перейти к содержимому

Интерфейсы как тип абстрактного класса c

  • автор:

Интерфейсы как тип абстрактного класса c

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

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

Когда следует использовать абстрактные классы:

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

Когда следует использовать интерфейсы:

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

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

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

public abstract class Vehicle < public abstract void Move(); >public class Car : Vehicle < public override void Move() < Console.WriteLine("Машина едет"); >> public class Bus : Vehicle < public override void Move() < Console.WriteLine("Автобус едет"); >> public class Tram : Vehicle < public override void Move() < Console.WriteLine("Трамвай едет"); >>

Абстрактный класс Vehicle определяет абстрактный метод перемещения Move() , а классы-наследники его реализуют.

Но, предположим, что наша система транспорта не ограничивается вышеперечисленными транспортными средствами. Например, мы можем добавить самолеты, лодки. Возможно, также мы добавим лошадь — животное, которое может также выполнять роль транспортного средства. Также можно добавить дирижабль. Вобщем получается довольно широкий круг объектов, которые связаны только тем, что являются транспортным средством и должны реализовать некоторый метод Move() , выполняющий перемещение.

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

Возможная реализация интерфейса могла бы выглядеть следующим образом:

public interface IMovable < void Move(); >public abstract class Vehicle : IMovable < public abstract void Move(); >public class Car : Vehicle < public override void Move() =>Console.WriteLine("Машина едет"); > public class Bus : Vehicle < public override void Move() =>Console.WriteLine("Автобус едет"); > public class Hourse : IMovable < public void Move() =>Console.WriteLine("Лошадь скачет"); > public class Aircraft : IMovable < public void Move() =>Console.WriteLine("Самолет летит"); >

Теперь метод Move() определяется в интерфейсе IMovable, а конкретные классы его реализуют.

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

Например, солнце, костер, батарея отопления и электрический нагреватель выполняют функцию нагревания или излучения тепла. По большому счету выделение тепла — это единственный общий между ними признак. Можно ли для них создать общий абстрактный класс? Можно, но это не будет оптимальным решением, тем более у нас могут быть какие-то родственные сущности, которые мы, возможно, тоже захотим использовать. Поэтому для каждой вышеперечисленной сущности мы можем определить свою систему классификации. Например, в одной системе классов, которые наследуются от общего астрактного класса, были бы звезды, в том числе и солнце, планеты, астероиды и так далее — то есть все те объекты, которые могут иметь какое-то общее с солнцем состояние. В рамках другой системы классов мы могли бы определить электрические приборы, в том числе электронагреатель. И так, для каждой разноплановой сущности можно было бы составить свою систему классов, исходяющую от определенного абстрактного класса. А для общего действия определить интерфейс, например, IHeatable, в котором бы был метод Heat, и этот интерфейс реализовать во всех необходимых классах.

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

Абстрактные классы и интерфейсы

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

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

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

Один класс может использовать несколько интерфейсов. Этим объясняется их популярность в Java, так как здесь отсутствием множественное наследование классов.

В Java, чтобы объявить класс абстрактным, надо в заголовке прописать слово abstract. Также обычно должен быть хотя бы один абстрактный метод. Рассмотрим пример:

public class AbstrTest  public static void main(String[] args)  UsualClass a = new UsualClass(); a.strPrint(); a.intPrint(); > > abstract class AbstrClass  abstract void strPrint(); void intPrint()  System.out.println(1); > > class UsualClass extends AbstrClass  void strPrint()  System.out.println("hi"); > >

В данном случае

  • Нельзя создавать объекты от класса AbstrClass, так как в его заголовке есть слово abstract. (Однако переменную такого типа можно было бы создать.)
  • Нельзя опустить реализацию метода strPrint() в классе UsualClass, поскольку он наследник абстрактного класса, в котором указанный метод объявлен абстрактным.
  • Абстрактные методы не имеют тел.
  • Если бы класс AbstrClass не содержал абстрактный strPrint(), или метод был бы не абстрактным, то в UsualClass можно было бы не переопределять данный метод. Таким образом, объявляя абстрактные методы, мы заставляем дочерние классы придерживаться определенного стандарта.
  • Абстрактный класс может не иметь абстрактных методов. Отличие такого класса от обычного родительского только в том, что от него нельзя создавать объекты.

При определении интерфейсов вместо class пишется ключевое слово interface. Если есть родительские интерфейсы, они также как в случае классов перечисляются после слова extends. У интерфейсов не может быть родительских классов.

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

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

Если класс является наследником как другого класса, так интерфейса, в его заголовке сначала пишется extends имя_класса, затем implements имя_интерфейса.

. interface InterFace  String getStr(); > class UsualClass extends AbstrClass implements InterFace  void strPrint()  System.out.println("hi"); > public String getStr()  return "HI"; > >

Если не указать слово public в реализации метода getStr(), будет ошибка.

Зачем нужны абстрактные методы, будь они в абстрактных классах или интерфейсах, если вся их реализация ложится на плечи дочерних классов? Если вы создаете группу порожденных от сестринских классов объектов, то можете присваивать их переменным типа абстрактного класса или интерфейса и обрабатывать всю группу, например, в одном цикле. У всей группы будут одни и те же методы, хотя реализация будет зависеть от типа объекта.

import java.util.ArrayList; public class ListObjects  public static void main(String[] args)  ArrayListAnimal> house = new ArrayList<>(); house.add(new Cat()); house.add(new Dog()); house.add(new Dog()); for (Animal animal : house)  animal.voice(); > > > abstract class Animal  abstract void voice(); > class Cat extends Animal  void voice()  System.out.println("Meow"); > > class Dog extends Animal  void voice()  System.out.println("Woof"); > >

Приведенная программа один раз мяукнет и два раза гавкнет, так как для каждого животного будет вызвана его реализация метода. Это также пример полиморфизма.

В случае наследования интерфейса было бы так:

. interface Animal  void voice(); > class Cat implements Animal  public void voice()  System.out.println("Meow"); > > class Dog implements Animal  public void voice()  System.out.println("Woof"); > >

В Java над переопределяемыми методами принято писать аннотацию @Override. Так при взгляде на класс сразу понятно, что метод не определяется, а переопределяется.

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

interface Animal  void voice(); default void drink()  System.out.println("I drink!"); > >

X Скрыть Наверх

Программирование на Java. Курс

Интерфейсы как тип абстрактного класса c

На этом шаге мы приведем общие сведения об интерфейсах .

На предыдущих шагах мы познакомились с базовыми принципами использования абстрактных классов. Как уже несколько раз отмечалось, использование абстрактного класса в качестве базового позволяет создавать производные классы «по одному шаблону» — то есть с одинаковым набором свойств и методов. Вместе с тем здесь имеется один «тонкий момент». Дело в том, что в языке C# запрещено множественное наследование: мы не можем создать производный класс на основе сразу нескольких базовых.

Множественное наследование есть в языке C++ . В языке C++ у производного класса может быть несколько базовых классов. В языках Java и C# от множественного наследования отказались в целях безопасности.

Это не очень хорошо, поскольку часто возникает необходимость «объединить в одно целое» сразу несколько классов. Например, в языке C++ , ставшем «прародителем» для языка C# , такая возможность существует. Это полезная возможность, но одновременно это и небезопасная возможность. Ведь разные классы описывались независимо друг от друга. Их объединение в один класс может привести к конфликтным ситуациям. Поэтому в языке C# от технологии множественного наследования отказались. Вместо множественного наследования используется другая технология, связанная с реализацией интерфейсов.

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

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

interface имя < // Тело интерфейса >

Начинается описание интерфейса с ключевого слова interface , после которого указывается название интерфейса, а в блоке из фигурных скобок объявляются методы, индексаторы и свойства.

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

Для методов указывается только сигнатура: тип результата, название метода и список аргументов. Ключевое слово abstract не указывается, как и ключевое слово virtual . По умолчанию объявленные в интерфейсе методы (а также свойства и индексаторы) считаются абстрактными и виртуальными. Спецификатор уровня доступа также не указывается. Все методы (свойства, индексаторы), объявленные в интерфейсе, являются открытыми (то есть будто бы описанными с ключевым словом public , хотя оно явно не используется).

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

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

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

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

class имя: интерфейс < // Тело класса >

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

class имя: интерфейс1, интерфейс2, . интерфейсN < // Тело класса >

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

class имя: базовый класс, интерфейс1, интерфейс2, . интерфейсN < // Тело класса >

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

На следующем шаге мы продолжим изучение этого вопроса .

Абстрактные классы и интерфейсы в 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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *