1. Основная концепция интерфейса
В Java «класс», украшенный ключевым словом interface, является интерфейсом.
Интерфейс определяется следующим образом:
interface 接口名称{
全局常量;
抽象方法;
}
2. Использование интерфейса
Использование интерфейса разделено на два этапа:
- Создать интерфейс
- реализовать интерфейс
2.1 Создать интерфейс
//源码
public interface Move {
void move();
}
2.2 Реализация интерфейса
//源码
public class Animal implements Move {
@Override
public void move() {
System.out.println("Animal move");
}
}
public class Human implements Move{
@Override
public void move() {
System.out.println("Human move");
}
}
public class Car implements Move {
@Override
public void move() {
System.out.println("Car move");
}
}
public class MoveTest {
public static void main(String[] args) {
Move [] move = {new Animal(), new Human(), new Car()};
for (Move m : move) {
m.move();
}
}
}
//执行结果
Animal move
Human move
Car move
3. Смысл интерфейса
Существуют две основные причины существования интерфейсов:
- Запрещено инстанцировать объекты непосредственно для него
- Преодолеть ограничение одиночного наследования (внедрить псевдомножественное наследование)
3.1 Запретить создание объектов непосредственно для него
На данный момент Java более ограничивает интерфейсы, чем абстрактные классы, потому что интерфейсы даже не имеют конструкторов, поэтому для них невозможно создавать экземпляры объектов.
//源码
public interface Move {
//此时编译器会提示 Interfaces cannot have constructors 错误信息
public Move(){}
void move();
}
3.2 Снять ограничение одиночного наследования (внедрить псевдомножественное наследование)
Поскольку в Java допускается несколько реализаций, после того, как класс реализует несколько интерфейсов, его можно преобразовать в несколько интерфейсов, то есть нарушить ограничение одиночного наследования.
//源码
public interface Fly {
void fly();
}
public interface Fight {
void fight();
}
public class SuperMan implements Fly,Fight{
@Override
public void fight() {
System.out.println("SuperMan fight");
}
@Override
public void fly() {
System.out.println("SuperMan fly");
}
}
public class MultiImplementsTest {
public static void main(String[] args) {
SuperMan sm = new SuperMan();
fly(sm);
fight(sm);
}
private static void fly(Fly f){
f.fly();
}
private static void fight(Fight f){
f.fight();
}
}
//执行结果
SuperMan fly
SuperMan fight
Поскольку SuperMan реализует интерфейсы Fly и Fight, SuperMan может быть преобразован с повышением частоты в интерфейс Fly или преобразован с повышением частоты в интерфейс Fight, то есть «множественное наследование?».
4. Запутанные понятия в интерфейсах
4.1 Есть ли в интерфейсах конструкторы?
В интерфейсе нет конструкторов. Подробнее см. в разделе «3.1 Запрет прямого создания объектов для него».
4.2 Может ли интерфейс наследовать обычный класс? Может ли интерфейс наследовать абстрактный класс?
Интерфейсы не могут наследовать от обычных классов.
//源码
public class Animal {
}
//此时开发工具会提示 The type Animal cannot be a superinterface of Fly; a superinterface must be an interface 错误信息
public interface Fly extends Animal{
void fly();
}
Интерфейсы не могут наследовать абстрактные классы.
//源码
public abstract class Airplane {
}
//此时开发工具会提示 The type Airplane cannot be a superinterface of Fly; a superinterface must be an interface 错误信息
public interface Fly extends Airplane{
void fly();
}
На самом деле это легко понять, ведь в интерфейсеТолькоНет таких строгих требований к определению статических констант и абстрактных методов, будь то обычные классы или абстрактные классы, поэтому интерфейсы не могут наследовать ни обычные классы, ни абстрактные классы.
4.3 Что происходит, когда метод в суперклассе реализующего класса совпадает с методом в интерфейсе?
4.3.1 Наследование классов при нормальных обстоятельствах
В Java подкласс класса наследует все методы и свойства родительского класса, которые украшены ключевыми словами public и protected.
//源码
public class Animal {
public void eat(){
System.out.println("Animal eat");
}
}
public class Tiger extends Animal{
}
public class TigerTest {
public static void main(String[] args) {
Tiger tiger = new Tiger();
tiger.eat();
}
}
//执行结果
Animal eat
4.3.2 Реализация интерфейса в обычных условиях
В Java, когда класс реализует интерфейс, он реализует все методы интерфейса.
//源码
public interface Fly {
void fly();
}
public class Eagle implements Fly {
@Override
public void fly() {
System.out.println("Eagle fly");
}
}
public class EagleTest {
public static void main(String[] args) {
Eagle eagle = new Eagle();
eagle.fly();
}
}
//执行结果
Eagle fly
4.3.3 Метод в родительском классе класса реализации отличается от метода в интерфейсе
//源码
public class Animal {
public void eat(){
System.out.println("Animal eat");
}
}
public interface Fly {
void fly();
}
public class Vulture extends Animal implements Fly {
@Override
public void fly() {
System.out.println("Vulture fly");
}
}
public class VultureTest {
public static void main(String[] args) {
Vulture vulture = new Vulture();
vulture.eat();
vulture.fly();
}
}
//执行结果
Animal eat
Vulture fly
4.3.4 Методы в родительском классе класса реализации такие же, как методы в интерфейсе
На самом деле обычно такого не происходит, если только программист не хочет напроситься на неприятности. Но если это для понимания концепции интерфейсов, то стоит попробовать. Как говорит Брюс Экель, автор книги «Thiking in Java»:
I generally find that once you know about a feature, you often discover places where it is useful.
После определения имени метода остается список параметров и тип возвращаемого значения метода, далее анализируем различные ситуации:
серийный номер | конкретная ситуация |
---|---|
1 | Те же параметры, то же возвращаемое значение |
2 | Те же параметры, разные возвращаемые значения |
3 | Параметры разные, возвращаемое значение одинаковое |
4 | Разные параметры, разные возвращаемые значения |
Ps: Здесь обсуждается предпосылка, что имена методов одинаковы.
4.3.4.1 Те же параметры, то же возвращаемое значение
//源码
public class Animal {
public void hunt(){
System.out.println("Animal hunt");
}
}
public interface Hunt {
void hunt();
}
public class Eagle extends Animal implements Hunt{
}
public class EagleTest {
public static void main(String[] args) {
Eagle eagle = new Eagle();
eagle.hunt();
}
}
//执行结果
Animal hunt
Вывод: если сигнатура и возвращаемое значение метода в суперклассе реализующего класса точно такие же, как сигнатура и возвращаемое значение метода в интерфейсе, то подкласс может явно реализовать метод в интерфейсе по-другому. Если в это время реализующий класс явно не реализует интерфейс, то будет вызван метод в родительском классе.
4.3.4.2 Одни и те же параметры, но разные возвращаемые значения
//源码
public class Animal {
public void hunt(){
System.out.println("Animal hunt");
}
}
public interface Hunt {
String hunt();
}
//此时,如果不实现接口中的方法,开发工具会提示 The return types are incompatible for the inherited methods Hunt.hunt(), Animal.hunt() 错误信息
public class Eagle extends Animal implements Hunt{
//此时开发工具会提示 The return type is incompatible with Animal.hunt() 错误信息
public String hunt(){
return "";
}
}
Вывод: Когда сигнатура метода в родительском классе класса реализации такая же, как и у метода в интерфейсе, но возвращаемое значение другое, определение класса реализации неудачно, т. е. нет такой (реализация) класс вообще.
4.3.4.3 Разные параметры, одно и то же возвращаемое значение
//源码
public class Animal {
public void hunt(){
System.out.println("Animal hunt");
}
}
public interface Hunt {
void hunt(String place);
}
public class Eagle extends Animal implements Hunt{
@Override
public void hunt(String place) {
System.out.println("Eagles hunt on the " + place);
}
}
public class EagleTest {
public static void main(String[] args) {
Eagle eagle = new Eagle();
eagle.hunt();
eagle.hunt("grasslands");
}
}
//执行结果
Animal hunt
Eagles hunt on the grasslands
Вывод: Когда параметры метода в родительском классе класса реализации отличаются от параметров метода в интерфейсе, но возвращаемое значение такое же, метод в интерфейсе необходимо перереализовать в классе реализации.
4.3.4.4 Разные параметры, разные возвращаемые значения
//源码
public class Animal {
public void hunt(){
System.out.println("Animal hunt");
}
}
public interface Hunt {
String hunt(String place);
}
public class Eagle extends Animal implements Hunt{
@Override
public String hunt(String place) {
System.out.println("Eagles hunt on the " + place);
return place;
}
}
public class EagleTest {
public static void main(String[] args) {
Eagle eagle = new Eagle();
eagle.hunt();
System.out.println(eagle.hunt("grasslands"));
}
}
//执行结果
Animal hunt
Eagles hunt on the grasslands
grasslands
Вывод: когда параметры и возвращаемое значение метода в родительском классе класса реализации отличаются от параметров и возвращаемого значения метода в интерфейсе, метод в интерфейсе необходимо повторно реализовать в классе реализации.
Причина, по которой приведенные выше концепции неясны, заключается в том, что понимание определения класса недостаточно тщательное:
class 类名称 extends 父类名称 implements 接口名称{
属性;
方法;
}
Из вышеприведенного определения мы знаем, что:Подкласс сначала наследует родительский класс, а затем реализует интерфейс. Код в «4.3.4.1 Те же параметры и то же возвращаемое значение» также подтверждает это, потому что если он реализован сначала, а затем наследуется, то в классе Eagle «4.3.4.1 Те же параметры и то же возвращаемое значение», интерфейс Hunt должен быть реализован.Метод здесь не реализован, и средство разработки не сообщает об ошибке, а это значит, что он был определен до реализации, и на данный момент метод в интерфейсе Hunt фактически не явно определены в классе Eagle, поэтому вывод может быть определен.Подкласс сначала наследует родительский класс, а затем реализует интерфейсверно.
ПонялПодкласс сначала наследует родительский класс, а затем реализует интерфейсПосле этого хорошо понятны приведенные выше выводы, такие как вывод «4.3.4.2 Одни и те же параметры, разные возвращаемые значения»:
Когда сигнатура метода в родительском классе класса реализации такая же, как и у метода в интерфейсе, но возвращаемое значение другое, определение класса реализации неудачно, то есть такого (реализации) нет. вообще класс.
Поскольку класс реализации сначала наследует родительский класс, а затем реализует интерфейс, когда класс реализации наследует родительский класс, это эквивалентно определению метода с той же сигнатурой метода, что и сигнатура метода в интерфейсе. В настоящее время единственная разница между ними заключается в том, что возвращаемое значение отличается, а возвращаемое значение отличается и не может различать два метода, то есть условие перегрузки метода не выполняется (имя метода такое же, тип или номер параметра отличается), поэтому он не может быть определен таким образом, тип. По той же причине, вот почему «4.3.4.3 Разные параметры, одно и то же возвращаемое значение» и «4.3.4.4 Разные параметры, другое возвращаемое значение» необходимо переопределить (реализовать) метод поиска в классе Eagle — класс Eagle не Для реализации метода в интерфейсе Hunt все, что у него есть, — это метод в классе Animal, который перегружен методом в интерфейсе Hunt.
4.4 Можно ли определить интерфейс в обычном классе?
Может.
//源码
public class Animal {
interface Climb{
}
}
4.5 Можно ли определить интерфейс в интерфейсе?
Может.
//源码
public interface Hunt {
interface Kill{
}
}
4.6 Как использовать приватный интерфейс, определенный в обычном классе?
//源码
public class Animal {
private Climb mClimb;
public interface Hunt{
void hunt();
}
private interface Climb{
void climb();
}
public class ClimbImpl implements Climb{
@Override
public void climb() {
System.out.println("ClimbImpl climb");
}
}
public Climb getClimb(){
return new ClimbImpl();
}
public void setClimb(Climb climb){
this.mClimb = climb;
mClimb.climb();
}
}
public class Eagle implements Animal.Hunt {
@Override
public void hunt() {
System.out.println("Eagle hunt");
}
}
//此时开发工具会提示 The type Animal.Climb is not visible 错误
public class Tortoise implements Animal.Climb {
}
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Animal();
System.out.println(animal.toString());
Animal.Hunt hunt = new Eagle();
hunt.hunt();
//Climb cannot be resolved to a type
// Climb climb = animal.getClimb();
animal.setClimb(animal.getClimb());
}
}
//执行结果
com.smart.www.define_interface_in_class_one.Animal@7852e922
Eagle hunt
ClimbImpl climb
Последнийanimal.setClimb(animal.getClimb())
Хотя метод немного навязчив, основной смысл здесь в том, что действительно можно использовать приватный интерфейс, определенный внутри класса, снаружи.
5. Практическое применение интерфейса
Сценариев использования интерфейса так много, что мы не можем проанализировать все ситуации. Далее мы проанализируем, как использовать интерфейсы в реальной разработке с точки зрения шаблонов проектирования.
Шаблон фабричного метода является широко используемым шаблоном создания, который может хорошо отделить создание объектов от использования объектов, а добавление новых типов объектов продукта не повлияет на исходный код. Далее мы анализируем, как применить интерфейс в шаблоне проектирования фабричный метод.
Шаблон фабричного метода определяется следующим образом:
Определите интерфейс для создания объектов продукта в родительском классе фабрики и позвольте подклассу отвечать за создание конкретных объектов продукта. (Определите интерфейс для создания объекта, но позвольте подклассам решать, какой класс создавать экземпляр. Фабричный метод позволяет классу отложить создание экземпляра до подкласса.)
Структурная схема паттерна фабричный метод выглядит следующим образом:
Как видно из приведенной выше структурной схемы, паттерн фабричный метод состоит из четырех частей:
- Продукт (абстрактный класс продукта)
Абстрактный класс продукта — это интерфейс, который определяет продукт и является «родительским классом» объекта продукта, созданного с помощью шаблона фабричного метода.
- ConcreteProduct (конкретная категория продуктов)
Конкретный класс продукта — это класс реализации абстрактного класса продукта, который реализует абстрактный класс продукта и полностью соответствует классу конкретной фабрики.
- Factory (абстрактный класс factory)
Абстрактный фабричный класс — это интерфейс, определяющий создание классов продуктов.
- ConcreteFactory (класс бетонного завода)
Класс конкретной фабрики — это класс реализации абстрактного класса фабрики, который реализует класс абстрактной фабрики и используется для производства конкретных продуктов, которые полностью соответствуют классам конкретных продуктов.
Затем мы создаем фабрику, которая может производить мобильные телефоны по шаблону фабричного метода.Схема структуры выглядит следующим образом:
Ниже приведена конкретная реализация кода:
//源码
public interface Phone {
void call();
}
public class IPhone implements Phone {
@Override
public void call() {
System.out.println("IPhone call");
}
}
public class XiaoMi implements Phone {
@Override
public void call() {
System.out.println("XiaoMi call");
}
}
public class MeiZu implements Phone {
@Override
public void call() {
System.out.println("MeiZu call");
}
}
public interface PhoneFactory {
Phone producePhone();
}
public class IPhoneFactory implements PhoneFactory {
@Override
public Phone producePhone() {
return new IPhone();
}
}
public class XiaoMiFactory implements PhoneFactory {
@Override
public Phone producePhone() {
return new XiaoMi();
}
}
public class MeiZuFactory implements PhoneFactory {
@Override
public Phone producePhone() {
return new MeiZu();
}
}
public class PhoneFactoryTest {
public static void main(String[] args) {
PhoneFactory phoneFactory = new XiaoMiFactory();
Phone phone = phoneFactory.producePhone();
phone.call();
}
}
//执行结果
XiaoMi call
Также очень удобно добавлять новую категорию продуктов, просто создайте новую категорию продуктов и новую фабрику, без какого-либо влияния на исходный код, например, добавив метод создания мобильного телефона Huawei для фабрики:
//源码
public class HuaWei implements Phone {
@Override
public void call() {
System.out.println("HuaWei call");
}
}
public class HuaWeiFactory implements PhoneFactory {
@Override
public Phone producePhone() {
return new HuaWei();
}
}
public class PhoneFactoryTest {
public static void main(String[] args) {
PhoneFactory phoneFactory = new HuaWeiFactory();
Phone phone = phoneFactory.producePhone();
phone.call();
}
}
//执行结果
HuaWei call
Справочная документация
- «Практическая классика Java-разработки»
- Мышление на Java
- "Шаблоны проектирования"
- Java Tutorials
- Design Principles