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

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

I will discuss following points in this post:

What are default methods in java 8?
Why default methods were needed in java 8?
How conflicts are resolved while calling default methods?

Как определять методы?

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

Давайте разберемся с примером:

public interface Moveable {
    default void move(){
        System.out.println("I am moving");
    }
}

Подвижный интерфейс определяет метод move(); и предоставил реализацию по умолчанию. Если какой-либо класс реализует этот интерфейс, ему не нужно реализовывать собственную версию метода move(). Он может напрямую вызывать instance.move();

public class Animal implements Moveable{
    public static void main(String[] args){
        Animal tiger = new Animal();
        tiger.move();
    }
}

Output: I am moving

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

public class Animal implements Moveable{
    
    public void move(){
        System.out.println("I am running");
    }
    
    public static void main(String[] args){
        Animal tiger = new Animal();
        tiger.move();
    }
}

Output: I am running

Это еще не все сделано здесь. Лучшая часть имеет следующие преимущества:

Почему так необходимы в Java 8?

Самый простой ответ — включить функциональность лямбда-выражения в Java. Они в основном имеют тип функционального интерфейса. Для поддержки их все основные классы должны быть изменены. Но эти базовые классы, такие как java.util.List, реализованы не только в классах JDK, но и в клиентском коде. Любое несовместимое изменение в основных классах наверняка вызовет ответную реакцию и не будет принято вообще.

Методы по умолчанию устраняют эту тупиковую ситуацию и позволяют добавлять поддержку функционального интерфейса в базовые классы. Давайте посмотрим на пример. Ниже приведен метод, который был добавлен в java.lang.Iterable.

default void forEach(Consumer<? super T> action) {
	Objects.requireNonNull(action);
	for (T t : this) {
		action.accept(t);
	}
}

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

Теперь, чтобы выполнить итерацию и выполнить простую операцию с каждым элементом в списке, все, что вам нужно сделать, это:

import java.util.ArrayList;
import java.util.List;

public class Animal implements Moveable{
    public static void main(String[] args){
        List<Animal> list = new ArrayList();
        list.add(new Animal());
        list.add(new Animal());
        list.add(new Animal());
        
        //Iterator code reduced to one line
        list.forEach((Moveable p) -> p.move());
    }
}

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

Как разрешаются конфликты при вызове?

Все идет нормально. У нас есть все основы хорошо. Теперь перейдем к сложным вещам. В Java класс может реализовать N номер интерфейса. Кроме того, интерфейс также может расширять другой интерфейс. Если какой-либо метод по умолчанию объявлен в двух таких интерфейсах, которые реализованы одним классом. тогда, очевидно, класс запутается, какой метод вызывать.

Правила для разрешения этого конфликта следующие:

1) Наиболее предпочтительными являются переопределенные методы в классах. Они будут сопоставлены и вызваны, если найдены, прежде чем сопоставить что-либо.

2) Выбран метод с той же сигнатурой в «наиболее конкретном интерфейсе, обеспечивающем по умолчанию». Это означает, что если класс Animal реализует два интерфейса, то есть Moveable и Walkable, так что Walkable расширяет Moveable. Тогда Walkable здесь является наиболее специфичным интерфейсом, и метод по умолчанию будет выбран здесь, если сигнатура метода совпадает.

3) Если Moveable и Walkable являются независимыми интерфейсами, возникает серьезное конфликтное состояние, и компилятор будет жаловаться, тогда он не сможет принять решение. Вы должны помочь компилятору, предоставив дополнительную информацию о том, из какого интерфейса должен вызываться метод по умолчанию.

	Walkable.super.move();
	//or 
	Moveable.super.move();

 

Оцените статью