Изменения в интерфейсе Java 8 – статический метод, метод по умолчанию

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

Интерфейс Java 8

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

Метод по умолчанию интерфейса Java

Для создания метода по умолчанию в интерфейсе Java нам нужно использовать ключевое слово “default” с сигнатурой метода. Например,

package com.journaldev.java8.defaultmethod;

public interface Interface1 {

	void method1(String str);
	
	default void log(String str){
		System.out.println("I1 logging::"+str);
	}
}

Обратите внимание, что log(String str) является методом по умолчанию в интерфейсе Interface1. Теперь, когда класс будет реализовывать Interface1, не обязательно предоставлять реализацию для методов по умолчанию интерфейса. Эта функция поможет нам расширять интерфейсы с дополнительными методами, все, что нам нужно, – это предоставить реализацию по умолчанию. Допустим, у нас есть еще один интерфейс со следующими методами:

package com.journaldev.java8.defaultmethod;

public interface Interface2 {

	void method2();
	
	default void log(String str){
		System.out.println("I2 logging::"+str);
	}

}

Мы знаем, что Java не позволяет нам расширять несколько классов, потому что это приведет к “Проблеме алмаза”, где компилятор не может решить, какой метод суперкласса использовать. С методами по умолчанию проблема алмаза возникает и для интерфейсов. Потому что если класс реализует как Interface1, так и Interface2 и не реализует общий метод по умолчанию, компилятор не может решить, какой выбрать. Расширение нескольких интерфейсов – это неотъемлемая часть Java, вы найдете это как в основных классах Java, так и в большинстве корпоративных приложений и фреймворков. Чтобы убедиться, что эта проблема не возникает в интерфейсах, обязательно предоставлять реализацию для общих методов по умолчанию интерфейсов. Поэтому, если класс реализует оба вышеприведенных интерфейса, ему придется предоставить реализацию для метода log(), в противном случае компилятор выдаст ошибку времени компиляции. Простой класс, реализующий как Interface1, так и Interface2, будет:

package com.journaldev.java8.defaultmethod;

public class MyClass implements Interface1, Interface2 {

	@Override
	public void method2() {
	}

	@Override
	public void method1(String str) {
	}

	@Override
	public void log(String str){
		System.out.println("MyClass logging::"+str);
		Interface1.print("abc");
	}
}

Важные моменты о методах по умолчанию в Java интерфейсах:

  1. Методы по умолчанию интерфейса Java помогут нам расширять интерфейсы, не боясь нарушить классы реализации.
  2. Методы по умолчанию интерфейса Java снизили различия между интерфейсами и абстрактными классами.
  3. Методы по умолчанию интерфейса Java 8 помогут нам избежать утилитарных классов, так как все методы класса Collections могут быть предоставлены в самих интерфейсах.
  4. Методы по умолчанию интерфейса Java помогут нам избавиться от базовых классов реализации, мы можем предоставить реализацию по умолчанию, и классы реализации могут выбрать, что переопределить.
  5. Одна из основных причин введения методов по умолчанию в интерфейсы – улучшение API коллекций в Java 8 для поддержки лямбда-выражений.
  6. Если у какого-либо класса в иерархии есть метод с той же сигнатурой, то методы по умолчанию становятся несущественными. Метод по умолчанию не может переопределить метод из \texttt{java.lang.Object}. Причина очень проста, потому что Object является базовым классом для всех классов Java. Поэтому даже если у нас есть методы класса Object, определенные как методы по умолчанию в интерфейсах, это будет бесполезно, потому что метод класса Object всегда будет использоваться. Поэтому, чтобы избежать путаницы, мы не можем иметь методы по умолчанию, которые переопределяют методы класса Object.
  7. Методы по умолчанию интерфейса Java также называются Методами защитника или Виртуальными методами расширения.

Статический метод интерфейса Java

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

package com.journaldev.java8.staticmethod;

public interface MyData {

	default void print(String str) {
		if (!isNull(str))
			System.out.println("MyData Print::" + str);
	}

	static boolean isNull(String str) {
		System.out.println("Interface Null Check");

		return str == null ? true : "".equals(str) ? true : false;
	}
}

Теперь давайте посмотрим на класс реализации, который имеет метод isNull() с плохой реализацией.

package com.journaldev.java8.staticmethod;

public class MyDataImpl implements MyData {

	public boolean isNull(String str) {
		System.out.println("Impl Null Check");

		return str == null ? true : false;
	}
	
	public static void main(String args[]){
		MyDataImpl obj = new MyDataImpl();
		obj.print("");
		obj.isNull("abc");
	}
}

Обратите внимание, что isNull(String str) – это простой классовый метод, он не переопределяет метод интерфейса. Например, если мы добавим @Override аннотацию к методу isNull(), это приведет к ошибке компилятора. Теперь, когда мы запустим приложение, мы получим следующий вывод.

Interface Null Check
Impl Null Check

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

Impl Null Check
MyData Print::
Impl Null Check

Статический метод интерфейса Java виден только методам интерфейса, если мы удалим метод isNull() из класса MyDataImpl, мы не сможем использовать его для объекта MyDataImpl. Однако, как и другие статические методы, мы можем использовать статические методы интерфейса, используя имя класса. Например, допустимым будет следующее утверждение:

boolean result = MyData.isNull("abc");

Важные моменты о статическом методе интерфейса Java:

  1. Статический метод интерфейса Java является частью интерфейса, мы не можем использовать его для объектов класса реализации.
  2. Статические методы интерфейса Java хороши для предоставления вспомогательных методов, например, проверки на null, сортировки коллекций и т. д.
  3. Методы статического интерфейса Java помогают нам обеспечить безопасность, не позволяя классам реализации переопределять их.
  4. Мы не можем определить статический метод интерфейса для методов класса Object, мы получим ошибку компилятора “Этот статический метод не может скрыть метод экземпляра из Object”. Это потому, что в Java это не разрешено, так как Object является базовым классом для всех классов, и мы не можем иметь статический метод уровня класса и другой метод экземпляра с тем же именем.
  5. Мы можем использовать статические методы интерфейса Java для удаления утилитарных классов, таких как Collections, и перемещения всех его статических методов в соответствующий интерфейс, что было бы легко найти и использовать.

Функциональные интерфейсы Java

russian
Перед тем, как завершить пост, я бы хотел предоставить краткое введение в функциональные интерфейсы. Интерфейс с ровно одним абстрактным методом известен как функциональный интерфейс. Для обозначения интерфейса как функционального была введена новая аннотация \(\text{}@FunctionalInterface\text{}\). Аннотация \(\text{}@FunctionalInterface\text{}\) представляет собой средство для предотвращения случайного добавления абстрактных методов в функциональные интерфейсы. Её использование необязательно, но это хорошая практика. Функциональные интерфейсы – долгожданная и очень востребованная особенность Java 8, поскольку они позволяют использовать \(\text{}лямбда-выражения\text{}\) для их создания. Также был добавлен новый пакет \(\text{java.util.function}\) с рядом функциональных интерфейсов для предоставления целевых типов для лямбда-выражений и ссылок на методы. Мы рассмотрим функциональные интерфейсы и лямбда-выражения в будущих постах.

Source:
https://www.digitalocean.com/community/tutorials/java-8-interface-changes-static-method-default-method