265 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			265 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| ---
 | ||
| title: Interfaces
 | ||
| localeTitle: Интерфейсы
 | ||
| ---
 | ||
| # Интерфейсы
 | ||
| 
 | ||
| Интерфейс в Java немного похож на класс, но с существенной разницей: `interface` может иметь _только_ сигнатуры методов, поля и методы по умолчанию. Начиная с Java 8, вы также можете создавать [методы по умолчанию](https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html) . В следующем блоке вы можете увидеть пример интерфейса:
 | ||
| 
 | ||
| ```java
 | ||
| public interface Vehicle { 
 | ||
|     public String licensePlate = ""; 
 | ||
|     public float maxVel 
 | ||
|     public void start(); 
 | ||
|     public void stop(); 
 | ||
|     default void blowHorn(){ 
 | ||
|       System.out.println("Blowing horn"); 
 | ||
|    } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| Интерфейс выше содержит два поля, два метода и метод по умолчанию. В одиночку это не очень полезно, но они обычно используются вместе с классами. Как? Простой, вы должны убедиться, что какой-то класс `implements` его.
 | ||
| 
 | ||
| ```java
 | ||
| public class Car implements Vehicle { 
 | ||
|     public void start() { 
 | ||
|         System.out.println("starting engine..."); 
 | ||
|     } 
 | ||
|     public void stop() { 
 | ||
|         System.out.println("stopping engine..."); 
 | ||
|     } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
|  [Код запуска](https://repl.it/CItd/0)
 | ||
| 
 | ||
| Теперь существует **основное правило** : класс должен реализовать **все** методы в интерфейсе. Методы должны иметь _одну и ту же_ подпись (имя, параметры и исключения), как описано в интерфейсе. Класс _не_ обязательно должен объявлять поля, но только методы.
 | ||
| 
 | ||
| ## Экземпляры интерфейса
 | ||
| 
 | ||
| После создания класса Java, который `implements` любой интерфейс, экземпляр объекта может ссылаться как экземпляр интерфейса. Эта концепция аналогична концепции экземпляра Inheritance.
 | ||
| 
 | ||
| ```java
 | ||
| // following our previous example 
 | ||
|  
 | ||
|  Vehicle tesla = new Car(); 
 | ||
|  
 | ||
|  tesla.start(); // starting engine ... 
 | ||
| ```
 | ||
| 
 | ||
| Интерфейс **не может** содержать методы конструктора, поэтому вы **не можете** создать экземпляр самого интерфейса. Вы должны создать экземпляр некоторого класса, реализующего интерфейс для его ссылки. Подумайте о интерфейсах в виде пустой формы контракта или шаблона.
 | ||
| 
 | ||
| Что вы можете сделать с этой функцией? Полиморфизм! Вы можете использовать только интерфейсы для обращения к экземплярам объектов!
 | ||
| 
 | ||
| ```java
 | ||
| class Truck implements Vehicle { 
 | ||
|     public void start() { 
 | ||
|         System.out.println("starting truck engine..."); 
 | ||
|     } 
 | ||
|     public void stop() { 
 | ||
|         System.out.println("stopping truck engine..."); 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  class Starter { 
 | ||
|     // static method, can be called without instantiating the class 
 | ||
|     public static void startEngine(Vehicle vehicle) { 
 | ||
|         vehicle.start(); 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  Vehicle tesla = new Car(); 
 | ||
|  Vehicle tata = new Truck(); 
 | ||
|  
 | ||
|  Starter.startEngine(tesla); // starting engine ... 
 | ||
|  Starter.startEngine(tata); // starting truck engine ... 
 | ||
| ```
 | ||
| 
 | ||
|  [Код запуска](https://repl.it/CItm/0)
 | ||
| 
 | ||
| ## Но как насчет нескольких интерфейсов?
 | ||
| 
 | ||
| Да, вы можете реализовать несколько интерфейсов в одном классе. В то время как в [Inheritance](//forum.freecodecamp.com/t/java-docs-inheritance) внутри Classes вам было запрещено наследовать только один класс, здесь вы можете расширить любое количество интерфейсов. Но не забудьте реализовать _все_ методы всех интерфейсов, иначе компиляция завершится неудачно!
 | ||
| 
 | ||
| ```java
 | ||
| public interface GPS { 
 | ||
|     public void getCoordinates(); 
 | ||
|  } 
 | ||
|  
 | ||
|  public interface Radio { 
 | ||
|     public void startRadio(); 
 | ||
|     public void stopRadio(); 
 | ||
|  } 
 | ||
|  
 | ||
|  public class Smartphone implements GPS,Radio { 
 | ||
|     public void getCoordinates() { 
 | ||
|         // return some coordinates 
 | ||
|     } 
 | ||
|     public void startRadio() { 
 | ||
|       // start Radio 
 | ||
|     } 
 | ||
|     public void stopRadio() { 
 | ||
|         // stop Radio 
 | ||
|     } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
|  [Код запуска](https://repl.it/CIto/0)
 | ||
| 
 | ||
| ## Некоторые функции интерфейсов
 | ||
| 
 | ||
| *   Вы можете размещать переменные в интерфейсе, хотя это не будет разумным решением, поскольку классы не должны иметь одну и ту же переменную. Короче говоря, избегайте размещения переменных!
 | ||
| *   Все переменные и методы в интерфейсе являются общедоступными, даже если вы не используете ключевое слово `public` .
 | ||
| *   Интерфейс не может указать реализацию конкретного метода. Это до Классы, чтобы сделать это. Несмотря на недавнее исключение (см. Ниже).
 | ||
| *   Если класс реализует несколько интерфейсов, тогда существует дистанционная вероятность перекрытия сигнатуры метода. Так как Java не допускает нескольких методов одной и той же сигнатуры, это может привести к проблемам. См. [Этот вопрос](http://stackoverflow.com/questions/2598009/method-name-collision-in-interface-implementation-java) для получения дополнительной информации.
 | ||
| 
 | ||
| ## Методы интерфейса по умолчанию
 | ||
| 
 | ||
| До Java 8 у нас не было никакого способа направить интерфейс для реализации конкретного метода. Это приводит к большому путанице и разрыву кода, если определение интерфейса внезапно изменяется.
 | ||
| 
 | ||
| Предположим, вы написали библиотеку с открытым исходным кодом, которая содержит интерфейс. Скажем, ваши клиенты, т.е. практически все разработчики по всему миру, используют его в большой степени и счастливы. Теперь вам нужно обновить библиотеку, добавив новое определение метода в интерфейс для поддержки новой функции. Но это сломает _все_ сборки, поскольку все классы, реализующие этот интерфейс, должны теперь измениться. Какая катастрофа!
 | ||
| 
 | ||
| К счастью, Java 8 теперь предоставляет нам методы по `default` для интерфейсов. Метод по `default` _может_ содержать собственную реализацию _непосредственно_ в интерфейсе! Итак, если класс не реализует метод по умолчанию, компилятор выполнит реализацию, упомянутую в интерфейсе. Приятно, не так ли? Поэтому в вашей библиотеке вы можете добавить любое количество методов по умолчанию в интерфейсах, не опасаясь ничего сломать!
 | ||
| 
 | ||
| ```java
 | ||
| public interface GPS { 
 | ||
|     public void getCoordinates(); 
 | ||
|     default public void getRoughCoordinates() { 
 | ||
|         // implementation to return coordinates from rough sources 
 | ||
|         // such as wifi & mobile 
 | ||
|         System.out.println("Fetching rough coordinates..."); 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  public interface Radio { 
 | ||
|     public void startRadio(); 
 | ||
|     public void stopRadio(); 
 | ||
|  } 
 | ||
|  
 | ||
|  public class Smartphone implements GPS,Radio { 
 | ||
|     public void getCoordinates() { 
 | ||
|         // return some coordinates 
 | ||
|     } 
 | ||
|     public void startRadio() { 
 | ||
|       // start Radio 
 | ||
|     } 
 | ||
|     public void stopRadio() { 
 | ||
|         // stop Radio 
 | ||
|     } 
 | ||
|  
 | ||
|     // no implementation of getRoughCoordinates() 
 | ||
|  } 
 | ||
|  
 | ||
|  Smartphone motoG = new Smartphone(); 
 | ||
|  motog.getRoughCoordinates(); // Fetching rough coordinates... 
 | ||
| ```
 | ||
| 
 | ||
|  [Код запуска](https://repl.it/CItp/0)
 | ||
| 
 | ||
| ### Но что произойдет, если два интерфейса имеют одну и ту же подпись метода?
 | ||
| 
 | ||
| Удивительный вопрос. В этом случае, если вы не предоставите реализацию в классе, плохой компилятор будет запутан и просто провалится! Вы также должны обеспечить реализацию метода по умолчанию в классе. Существует также отличный способ использовать `super` чтобы назвать, какая реализация вам нравится:
 | ||
| 
 | ||
| ```java
 | ||
| public interface Radio { 
 | ||
|     // public void startRadio(); 
 | ||
|     // public void stopRadio(); 
 | ||
|  
 | ||
|     default public void next() { 
 | ||
|         System.out.println("Next from Radio"); 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  public interface MusicPlayer { 
 | ||
|     // public void start(); 
 | ||
|     // public void pause(); 
 | ||
|     // public void stop(); 
 | ||
|  
 | ||
|     default public void next() { 
 | ||
|         System.out.println("Next from MusicPlayer"); 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  public class Smartphone implements Radio, MusicPlayer { 
 | ||
|     public void next() { 
 | ||
|         // Suppose you want to call MusicPlayer next 
 | ||
|         MusicPlayer.super.next(); 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  Smartphone motoG = new Smartphone(); 
 | ||
|  motoG.next(); // Next from MusicPlayer 
 | ||
| ```
 | ||
| 
 | ||
|  [Код запуска](https://repl.it/CIts/0)
 | ||
| 
 | ||
| ## Статические методы в интерфейсах
 | ||
| 
 | ||
| Также новым для Java 8 является возможность добавления статических методов в интерфейсы. Статические методы в интерфейсах почти идентичны статическим методам в конкретных классах. Единственное большое различие заключается в том, что `static` методы не наследуются в классах, реализующих интерфейс. Это означает, что интерфейс ссылается при вызове статического метода, а не в классе, который его реализует.
 | ||
| 
 | ||
| ```java
 | ||
| interface MusicPlayer { 
 | ||
|   public static void commercial(String sponsor) { 
 | ||
|     System.out.println("Now for a message brought to you by " + sponsor); 
 | ||
|   } 
 | ||
|  
 | ||
|   public void play(); 
 | ||
|  } 
 | ||
|  
 | ||
|  
 | ||
|  class Smartphone implements MusicPlayer { 
 | ||
|     public void play() { 
 | ||
|         System.out.println("Playing from smartphone"); 
 | ||
|     } 
 | ||
|  } 
 | ||
|  
 | ||
|  class Main { 
 | ||
|   public static void main(String[] args) { 
 | ||
|     Smartphone motoG = new Smartphone(); 
 | ||
|     MusicPlayer.commercial("Motorola"); // Called on interface not on implementing class 
 | ||
|     // motoG.commercial("Motorola"); // This would cause a compilation error 
 | ||
|   } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
|  [Код запуска](https://repl.it/CIts/9)
 | ||
| 
 | ||
| ## Наследование интерфейса
 | ||
| 
 | ||
| В Java также возможно, чтобы интерфейс _наследовал_ другой интерфейс, используя, как вы уже догадались, `extends` ключевое слово:
 | ||
| 
 | ||
| ```java
 | ||
| public interface Player { 
 | ||
|     public void start(); 
 | ||
|     public void pause(); 
 | ||
|     public void stop(); 
 | ||
|  } 
 | ||
|  
 | ||
|  public interface MusicPlayer extends Player { 
 | ||
|     default public void next() { 
 | ||
|         System.out.println("Next from MusicPlayer"); 
 | ||
|     } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
| Это означает, что класс, реализующий интерфейс `MusicPlayer` должен реализовывать _все_ методы `MusicPlayer` а также `Player` :
 | ||
| 
 | ||
| ```java
 | ||
| public class SmartPhone implements MusicPlayer { 
 | ||
|     public void start() { 
 | ||
|         System.out.println("start"); 
 | ||
|     } 
 | ||
|     public void stop() { 
 | ||
|         System.out.println("stop"); 
 | ||
|     } 
 | ||
|     public void pause() { 
 | ||
|         System.out.println("pause"); 
 | ||
|     } 
 | ||
|  } 
 | ||
| ```
 | ||
| 
 | ||
|  [Код запуска](https://repl.it/CIty/0)
 | ||
| 
 | ||
| Ой, я забыл `next()` ? См., Поскольку это был метод по `default` , мне не пришлось реализовывать реализацию вообще. (Не будет работать для JDK <8)
 | ||
| 
 | ||
| Итак, теперь у вас есть хорошее понимание интерфейсов! Познакомьтесь с абстрактными классами, чтобы узнать, как Java дает вам еще один способ определения контрактов. |