Java 8 #1: metody domyślne i statyczne w interfejsach

Interfejsy w JDK 7

Zanim zacznę opisywać zmiany jakie zaszły w interfejsach w Javie w wersji ósmej, małe podsumowanie, jak działały one w poprzednich wersjach.

Właściwości interfejsów w JDK 7:

  • interfejs implementujesz w klasie a nie dziedziczysz (jak w przypadku klas abstrakcyjnych)
  • interfejs może być rozszerzony o inne interfejsy (więcej niż jeden, w przeciwieństwie do klas, które mogą dziedziczyć tylko po jednej klasie)
  • jedna klasa może implementować dowolnie wiele interfejsów
  • domyślnie dostęp do składowych interfejsu jest publiczny
  • interfejs może przetrzymywać stałe
  • głównym celem interfejsu jest deklaracja metod (składowanie ich sygnatur). Metody te niejawnie są abstrakcyjne (czyli bez ciała).

Metody statyczne

Nie ma tu wielkiej filozofii. Po prostu od teraz możesz pisać publiczne metody statyczne w swoim interfejsie. Nie łamie to idei interfejsu (czyli bezstanowości), ponieważ metoda statyczna należy do klasy (w tym przypadku interfejsu) a nie do obiektu (instancji).

Przykładowy interfejs zawierający stałą, jedną sygnaturę i metodę statyczną.

public interface Flying {
	public static final String NAME = "Flying interface";
	
	public void fly();
	public static boolean isAllowToFly(String weather) {
		return "good".equals(weather) ? true : false;
	}
}

Metody domyślnie

Kolejną nowością jest używanie metod domyślnych. Przypominają one powyższe metody statyczne, tzn. po zaimplementowaniu interfejsu w swojej klasie, możesz odwoływać się do tej metody. Ważna różnica pomiędzy nimi, jest to, że metody domyślne tak jak każde metody instancyjne (niestatyczne) można nadpisywać. Najprościej metodę domyślną można rozumieć jako podstawową implementację danej sygnatury, zawartej już w samym interfejsie.

public interface Flying {
	public static final String NAME = "Flying interface";
	
	public void fly();
	public static boolean isAllowToFly(String weather) {
		return "good".equals(weather) ? true : false;
	}
	
	default void showName(){  
        System.out.println(NAME);  
    } 
}

Przykład klasy, która nadpisuje domyślną metodę showname.

public class Plane implements Flying{

	@Override
	public void fly() {
		System.out.println("fly..");
	}
	
	@Override
	public void showName(){  
		System.out.println("Show plane"); 
    }
}

Pewnym problemem wynikającym z posiadania metod domyślnych może być posiadanie konfliktów przy używania tych samych nazw w różnych interfejsach.

public interface Landing {
	public static final String NAME = "Landing interface";
	
	public void land();
	
	default void showName(){  
        System.out.println(NAME);  
    } 
}

Mój nowy interfejs posiada tą samą metodę domyślną jak interfejs Flying. Co się stanie w przypadku implementacji obu interfejsów w tej samej klasie? W takim przypadku kompilator zgłosi błąd:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 
	Duplicate default methods named showName with the parameters () and () are inherited from the types Landing and Flying

Tego typu problem nazywa się problemem diamentu (ang. diamond problem*). Aby go obejść można nadpisać skonfliktowaną metodę i w jej ciele wybrać, którą implementację wybierasz.

public class PlaneConflict implements Flying, Landing{

	@Override
	public void land() {}

	@Override
	public void fly() {}

	@Override
	public void showName() {
		Flying.super.showName();
	}
}

Interfejsy funkcyjne

Ostatnią ciekawą kwestią dodaną do JDK 8 jest możliwość tworzenia interfejsów funkcyjnych, ale ten temat omówię już w kolejnej lekcji.

* https://en.wikipedia.org/wiki/Multiple_inheritance

Kod do lekcji: https://github.com/developeronthego/java-jdk8/tree/master/src/main/java/java8/interfaces

Może Ci się również spodoba

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *