Implements java что это
Перейти к содержимому

Implements java что это

  • автор:

Implements java что это

В этой статье сделана попытка объяснить некоторые термины объектно-ориентированного программирования Java, и ответить на вопросы: что значит слово extends в определении класса? Что значит слово implements в определении класса? В чем разница между extends и implements? Что такое interface? Что такое @Override?

Если коротко, то:

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

implements это ключевое слово, предназначенное для реализации интерфейса (interface).

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

Тут появилось словечко интерфейс (interface). Разница между interface и обычным классом (regular class) — то, что в интерфейсе Вы не можете определить определенную реализацию (только ее «интерфейс»), а в классе можете. Если сказать точнее, то это означает, что в интерфейсе Вы можете только указать методы, но не реализовывать их. Только класс может реализовать (implement) интерфейс. Класс также может расширить (extend) другой класс. Аналогично, интерфейс может расширить другой интерфейс. Реализация (implements) используется для интерфейса, и расширение (extends) используется для расширения класса. Когда Вы должны выбрать между реализацией интерфейса или расширением класса, пойдите по пути реализации интерфейса, так как класс может реализовать множество интерфейсов, но расширить можно только один класс.

Java не поддерживает множественное наследование (multiple inheritance) для классов. Эта проблема также решается путем использования нескольких интерфейсов.

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

Пример реализации интерфейса (как используется ключевое слово implements):

//Интерфейс, здесь нет реализации методов, // только их объявления: public interface ExampleInterface  public void do(); public String doThis(int number); > 

Интерфейс также может содержать в себе декларации полей констант, аннотации, интерфейсы и даже классы.

//А вот это уже реализация (применение ключевого слова implements): public class sub implements ExampleInterface  public void do()  //определите то, что должно произойти . > public String doThis(int number)  //определите то, что должно произойти . > > 

Теперь пример расширения класса (применение ключевого слова extends):

//Исходный класс, который будет расширен: public class SuperClass  public int getNb()  //определите то, что должно произойти return 1; > public int getNb2()  //определите то, что должно произойти return 2; > >
//Производный класс, расширяющий исходный: public class SubClass extends SuperClass //Вы можете переназначить (override) реализацию метода: @Override public int getNb2() return 3; > >

Вот что получится в результате:

SubClass s = new SubClass(); s.getNb(); //возвращает 1 s.getNb2(); //возвращает 3
SuperClass sup = new SuperClass(); sup.getNb(); //возвращает 1 sup.getNb2(); //возвращает 2

Чтобы лучше понять работу терминов extends, implements, interface, @Override, необходимо изучить принципы объектно-ориентированного программирования: динамическое связывание (dynamic binding), полиморфизм (polymorphism) и общее наследование (general inheritance) [1].

[Ссылки]

1. Lesson: Interfaces and Inheritance site:docs.oracle.com .

Implements java что это

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

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

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

interface Printable

Данный интерфейс называется Printable. Интерфейс может определять константы и методы, которые могут иметь, а могут и не иметь реализации. Методы без реализации похожи на абстрактные методы абстрактных классов. Так, в данном случае объявлен один метод, который не имеет реализации.

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

Чтобы класс применил интерфейс, надо использовать ключевое слово implements :

public class Program < public static void main(String[] args) < Book b1 = new Book("Java. Complete Referense.", "H. Shildt"); b1.print(); >> interface Printable < void print(); >class Book implements Printable < String name; String author; Book(String name, String author)< this.name = name; this.author = author; >public void print() < System.out.printf("%s (%s) \n", name, author); >>

В данном случае класс Book реализует интерфейс Printable. При этом надо учитывать, что если класс применяет интерфейс, то он должен реализовать все методы интерфейса, как в случае выше реализован метод print . Потом в методе main мы можем создать объект класса Book и вызвать его метод print. Если класс не реализует какие-то методы интерфейса, то такой класс должен быть определен как абстрактный, а его неабстрактные классы-наследники затем должны будут реализовать эти методы.

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

Printable pr = new Printable(); pr.print();

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

class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >public void print() < System.out.println(name); >>

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

public class Program < public static void main(String[] args) < Printable printable = new Book("Java. Complete Reference", "H. Shildt"); printable.print(); // Java. Complete Reference (H. Shildt) printable = new Journal("Foreign Policy"); printable.print(); // Foreign Policy >> interface Printable < void print(); >class Book implements Printable < String name; String author; Book(String name, String author)< this.name = name; this.author = author; >public void print() < System.out.printf("%s (%s) \n", name, author); >> class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >public void print() < System.out.println(name); >>

Интерфейсы в преобразованиях типов

Все сказанное в отношении преобразования типов характерно и для интерфейсов. Например, так как класс Journal реализует интерфейс Printable, то переменная типа Printable может хранить ссылку на объект типа Journal:

Printable p =new Journal("Foreign Affairs"); p.print(); // Интерфейс не имеет метода getName, необходимо явное приведение String name = ((Journal)p).getName(); System.out.println(name);

И если мы хотим обратиться к методам класса Journal, которые определены не в интерфейсе Printable, а в самом классе Journal, то нам надо явным образом выполнить преобразование типов: ((Journal)p).getName();

Методы по умолчанию

Ранее до JDK 8 при реализации интерфейса мы должны были обязательно реализовать все его методы в классе. А сам интерфейс мог содержать только определения методов без конкретной реализации. В JDK 8 была добавлена такая функциональность как методы по умолчанию . И теперь интерфейсы кроме определения методов могут иметь их реализацию по умолчанию, которая используется, если класс, реализующий данный интерфейс, не реализует метод. Например, создадим метод по умолчанию в интерфейсе Printable:

interface Printable < default void print()< System.out.println("Undefined printable"); >>

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

class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >>

Статические методы

Начиная с JDK 8 в интерфейсах доступны статические методы — они аналогичны методам класса:

interface Printable < void print(); static void read()< System.out.println("Read printable"); >>

Чтобы обратиться к статическому методу интерфейса также, как и в случае с классами, пишут название интерфейса и метод:

public static void main(String[] args)

Приватные методы

По умолчанию все методы в интерфейсе фактически имеют модификатор public. Однако начиная с Java 9 мы также можем определять в интерфейсе методы с модификатором private . Они могут быть статическими и нестатическими, но они не могут иметь реализации по умолчанию.

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

public class Program < public static void main(String[] args) < Calculatable c = new Calculation(); System.out.println(c.sum(1, 2)); System.out.println(c.sum(1, 2, 4)); >> class Calculation implements Calculatable < >interface Calculatable < default int sum(int a, int b)< return sumAll(a, b); >default int sum(int a, int b, int c) < return sumAll(a, b, c); >private int sumAll(int. values) < int result = 0; for(int n : values)< result += n; >return result; > >

Константы в интерфейсах

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

interface Stateable

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

public class Program < public static void main(String[] args) < WaterPipe pipe = new WaterPipe(); pipe.printState(1); >> class WaterPipe implements Stateable < public void printState(int n)< if(n==OPEN) System.out.println("Water is opened"); else if(n==CLOSED) System.out.println("Water is closed"); else System.out.println("State is invalid"); >> interface Stateable

Множественная реализация интерфейсов

Если нам надо применить в классе несколько интерфейсов, то они все перечисляются через запятую после слова implements:

interface Printable < // методы интерфейса >interface Searchable < // методы интерфейса >class Book implements Printable, Searchable < // реализация класса >

Наследование интерфейсов

Интерфейсы, как и классы, могут наследоваться:

interface BookPrintable extends Printable

При применении этого интерфейса класс Book должен будет реализовать как методы интерфейса BookPrintable, так и методы базового интерфейса Printable.

Вложенные интерфейсы

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

class Printer < interface Printable < void print(); >>

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

public class Journal implements Printer.Printable < String name; Journal(String name)< this.name = name; >public void print() < System.out.println(name); >>

Использование интерфейса будет аналогично предыдущим случаям:

Printer.Printable p =new Journal("Foreign Affairs"); p.print();

Интерфейсы как параметры и результаты методов

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

public class Program < public static void main(String[] args) < Printable printable = createPrintable("Foreign Affairs",false); printable.print(); read(new Book("Java for impatients", "Cay Horstmann")); read(new Journal("Java Dayly News")); >static void read(Printable p) < p.print(); >static Printable createPrintable(String name, boolean option) < if(option) return new Book(name, "Undefined"); else return new Journal(name); >> interface Printable < void print(); >class Book implements Printable < String name; String author; Book(String name, String author)< this.name = name; this.author = author; >public void print() < System.out.printf("%s (%s) \n", name, author); >> class Journal implements Printable < private String name; String getName()< return name; >Journal(String name) < this.name = name; >public void print() < System.out.println(name); >>

Метод read() в качестве параметра принимает объект интерфейса Printable, поэтому в этот метод мы можем передать как объект Book, так и объект Journal.

Метод createPrintable() возвращает объект Printable, поэтому также мы можем возвратить как объект Book, так и Journal.

Foreign Affairs Java for impatients (Cay Horstmann) Java Dayly News

Implements и Extends в Java

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

Extends или implements

Обсудим различия между обоими ключевыми словами.

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

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

Хотя оба ключевых слова согласуются с концепцией наследования, ключевое слово implements в первую очередь связано с абстракцией и используется для определения контракта, а extends используется для расширения существующей функциональности класса.

Реализация

Перейдем к реализации и подробно рассмотрим extends, implements и множественное наследование.

Extends

Начнем с создания класса Media с id, name и artist. Этот класс будет действовать как базовый класс. VideoMedia и AudioMedia расширят функциональность этого класса:

public class Media < private int id; private String title; private String artist; // стандартные геттеры и сеттеры >

Теперь создадим еще один класс с именем VideoMedia, который расширяет класс Media, наследуя его свойства. Кроме того, он имеет собственные свойства, такие как resolution и aspectRatio:

public class VideoMedia extends Media < private String resolution; private String aspectRatio; // стандартные геттеры и сеттеры >

Точно так же класс AudioMedia расширяет класс Media и будет иметь собственные дополнительные свойства, такие как bitrate и frequency:

public class AudioMedia extends Media < private int bitrate; private String frequency; // стандартные геттеры и сеттеры @Override public void printTitle() < System.out.println("AudioMedia Title"); >>

Создадим объекты для базового и производного классов, чтобы посмотреть на унаследованные свойства:

Media media = new Media(); media.setId(001); media.setTitle("Media1"); media.setArtist("Artist001"); AudioMedia audioMedia = new AudioMedia(); audioMedia.setId(101); audioMedia.setTitle("Audio1"); audioMedia.setArtist("Artist101"); audioMedia.setBitrate(3500); audioMedia.setFrequency("256kbps"); VideoMedia videoMedia = new VideoMedia(); videoMedia.setId(201); videoMedia.setTitle("Video1"); videoMedia.setArtist("Artist201"); videoMedia.setResolution("1024x768"); videoMedia.setAspectRatio("16:9"); System.out.println(media); System.out.println(audioMedia); System.out.println(videoMedia);

Все три класса выводят связанные свойства:

Media AudioMedia VideoMedia

Implements

Чтобы понять абстракцию и интерфейсы, создадим интерфейс MediaPlayer с двумя методами, называемыми play и pause. Как упоминалось ранее, все методы в этом интерфейсе являются абстрактными. Другими словами, интерфейс содержит только объявления методов.

В Java интерфейсы не должны явно объявлять метод как abstract или public. Классы, реализующие интерфейс MediaPlayer, будут определять следующие методы:

public interface MediaPlayer

Класс AudioMediaPlayer реализует MediaPlayer и определяет методы play и pause для аудиофайлов:

public class AudioMediaPlayer implements MediaPlayer < @Override public void play() < System.out.println("AudioMediaPlayer is Playing"); >@Override public void pause() < System.out.println("AudioMediaPlayer is Paused"); >>

Точно так же VideoMediaPlayer реализует MediaPlayer и предоставляет определение метода для воспроизведения и приостановки видео:

public class VideoMediaPlayer implements MediaPlayer < @Override public void play() < System.out.println("VideoMediaPlayer is Playing"); >@Override public void pause() < System.out.println("VideoMediaPlayer is Paused"); >>

Далее создадим экземпляры AudioMediaPlayer и VideoMediaPlayer и вызовем методы play и pause для них обоих:

AudioMediaPlayer audioMediaPlayer = new AudioMediaPlayer(); audioMediaPlayer.play(); audioMediaPlayer.pause(); VideoMediaPlayer videoMediaPlayer = new VideoMediaPlayer(); videoMediaPlayer.play(); videoMediaPlayer.pause();

AudioMediaPlayer и VideoMediaPlayer вызывают соответствующие реализации play и pause:

AudioMediaPlayer is Playing AudioMediaPlayer is Paused VideoMediaPlayer is Playing VideoMediaPlayer is Paused

Множественное наследование

Java не поддерживает множественное наследование напрямую из-за неоднозначности. Проблема неоднозначности возникает, когда класс наследуется более чем от одного родительского класса, и оба родительских класса имеют метод или свойство с одинаковым именем. Следовательно, дочерний класс не может разрешить конфликт наследуемого метода или свойства. Однако класс может наследовать от нескольких интерфейсов.

Создадим интерфейс AdvancedPlayerOptions:

public interface AdvancedPlayerOptions

Класс MultiMediaPlayer реализует MediaPlayer и AdvancedPlayerOptions и определяет методы, объявленные в обоих интерфейсах:

public class MultiMediaPlayer implements MediaPlayer, AdvancedPlayerOptions < @Override public void play() < System.out.println("MultiMediaPlayer is Playing"); >@Override public void pause() < System.out.println("MultiMediaPlayer is Paused"); >@Override public void seek() < System.out.println("MultiMediaPlayer is being seeked"); >@Override public void fastForward() < System.out.println("MultiMediaPlayer is being fast forwarded"); >>

Теперь создадим экземпляр класса MultiMediaPlayer и вызовем все реализованные методы:

MultiMediaPlayer multiMediaPlayer = new MultiMediaPlayer(); multiMediaPlayer.play(); multiMediaPlayer.pause(); multiMediaPlayer.seek(); multiMediaPlayer.fastForward();

Как и ожидалось, MultiMediaPlayer вызывает свои реализации play и pause:

MultiMediaPlayer is Playing MultiMediaPlayer is Paused MultiMediaPlayer is being seeked MultiMediaPlayer is being fast forwarded

Заключение

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

Код статьи доступен на GitHub.

Интерфейсы — Java: Введение в ООП

Классы в Java — основы основ, но их описание будет неполным без интерфейсов, с которыми они тесно связаны. Интерфейсы — более простая конструкция, но, как и в случае с классами, полное понимание интерфейсов требует опыта работы с ними. Зачем они нужны? Интерфейсы позволяют задавать требования к классам, то есть какие методы требуются от класса. Предположим, что мы хотим работать в приложении с генератором паролей. Генератор в нашем случае это обычный класс, с методом generate() , возвращающим пароль.

var generator = new SimplePasswordGenerator(); generator.generate(); // Возвращает готовый пароль generator.generate(); // Уже другой пароль 

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

Создадим интерфейс PasswordGenerator и опишем в нем два метода generate() . Один метод будет без параметров, другой — с возможностью настройки длины пароля:

interface PasswordGenerator  String generate(); String generate(int length); > 

Теперь интерфейс нужно реализовать. Делается это в определении класса:

// Ключевое слово implements означает, что класс реализует интерфейс PasswordGenerator class SimplePasswordGenerator implements PasswordGenerator  public String generate()  // Обращаемся к методу объекта // 16 – выбранное значение по умолчанию return this.generate(16); > public String generate(int length)  // Тут логика генерации простого пароля > > 

Интерфейсы не ограничивают класс в его расширении. Помимо интерфейсных методов, мы можем добавить и любые другие.

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

Хорошо, вот мы ввели интерфейс, что он нам дал? Если коротко: полиморфизм, а если точнее — полиморфизм подтипов (subtyping). Прямо сейчас он нам не очень нужен, но для общего развития попробуем ухватить его идею. Осторожно, дальнейший текст может напугать. Если он кажется вам сложным, просто пропустите, все это мы будем повторять еще не раз.

Код, который будет использовать passwordGenerator может выглядеть так:

class UserController  // Здесь создаются пользователи // Этот код вызывается где-то внутри приложения при регистрации пользователя public void create()  // Создаем пользователя // И где-то тут же генерируем ему пароль var generator = new SimplePasswordGenerator(); var password = generator.generate(); > > 

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

var generator; if (userChooseSomething)  generator = new SimplePasswordGenerator(); > else  generator = new SuperPasswordGenerator(); > 

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

Сделаем так, чтобы класс SuperPasswordGenerator() тоже реализовывал интерфейс PasswordGenerator :

class SuperPasswordGenerator implements PasswordGenerator  public String generate()  return this.generate(16); > public String generate(int length)  // Тут уже другая логика генерации супер-сложного пароля > > 

Код, который будет использовать PasswordGenerator , меняется на такой:

class UserController  // Интерфейс = Тип // Вместо конкретного класса указываем интерфейс public create(PasswordGenerator generator)  var password = generator.generate(); > > 

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

В коде выше видно, что мы требуем передавать в метод create тип PasswordGenerator . Таким типом будет любой объект, класс которого реализует интерфейс PasswordGenerator . Теперь мы можем передать в метод create() любой генератор паролей, который реализует интерфейс PasswordGenerator . В получившемся коде генератор создается не там, где используется. Он создается где-то раньше, а уже в код приходит нужная реализация.

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

Все это уже станет актуальным, как только мы дойдем до Java-коллекций.

Соглашения и правила

К интерфейсам предъявляются такие же требования как и к классам

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

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

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