1 Programowanie w Javie wykład 8 Interfejsy Treści prezentowane w wykładzie zostały oparte o: Barteczko, JAVA Programowanie praktyczne od podstaw, PWN, 2014 Barteczko, JAVA Uniwersalne techniki programowania, PWN, 2017 http://docs.oracle.com/javase/8/docs/ C. S. Horstmann, G. Cornell, Java. Podstawy, Helion, Gliwice 2013 S. Stępniak JDK 8 WPROWADZENIE DO WYBRANYCH ZAGADNIEŃ
INTERFEJSY 2 Interfejs (deklarowany za pomocą słowa kluczowego interface) to: zestaw publicznych abstrakcyjnych metod (ich nagłówków) i/lub publicznych stałych (statycznych) Poczynając od wersji 8 Javy, w definicji interfejsu mogą się znaleźć definicje publicznych metod statycznych oraz publicznych domyślnych metod niestatycznych, poprzedzone słowem kluczowym default Od Javy 9 interfejsy mogą ponadto zawierać metody prywatne. Implementacja interfejsu w klasie - to zdefiniowanie w tej klasie wszystkich abstrakcyjnych metod interfejsu. To że klasa ma implementować interfejs X oznaczamy słowem kluczowym implements X. Każda klasa implementująca interfejs musi zdefiniować WSZYSTKIE jego metody abstrakcyjne albo musi być zadeklarowana jako klasa abstrakcyjna. W Javie klasa (oprócz dziedziczenia innej klasy) może implementować dowolną liczbę interfejsów. Zatem w Javie nie ma wielodziedziczenia klas, ale za to jest możliwe wielodziedziczenie interfejsów.
Interfejsy 3 Ogólna postać definicji interfejsu : [public] interface NazwaInterfejsu [extends other interfaces]{ typ nazwazmiennej = wartosc;... typ nazwametody(lista_parametrów);... Uwagi: modyfikator dostępu public przed słowem interface może nie występować - wówczas interfejs jest dostępny tylko w bieżącym pakiecie, pola to zawsze publiczne stałe statyczne nawet jeśli nie użyjemy żadnych modyfikatorów kompilator traktuje je jako public static final metody to: metody abstrakcyjne (bez implementacji) (ponieważ do tej pory interfejsy zawierały tylko metody abstrakcyjne, to słowo abstract pomijamy), metody domyślne default, metody statyczne oraz metody prywatne Interfejsy - podobnie jak klasy - wyznaczają typy danych (referencyjne). Możemy korzystać z operatora instanceof oraz rzutowania.
Interfejsy 4 Ogólna postać definicji klasy implementującej interfejs: [modyfikator] class NazwaKlasy [extends KlasaBazowa] implements NazwaInterfejsu_1,..., NazwaInterfejsu_n{... Uwagi: klasa może implementować wiele interfejsów, klasa musi definiować wszystkie metody implementowanych interfejsów albo musi być zadeklarowana jako klasa abstrakcyjna klasa może zawierać własne (nie będące częścią interfejsu) pola i metody.
Interfejsy - przykład Przykład: Interfejs określający abstrakcyjną funkcjonalność "wydającego głos" : public interface Speakable { int QUIET = 0; int LOUD = 1; // <- publiczne stałe statyczne // domyślnie public static final String getvoice(int voice); // <- metoda abstrakcyjna; // ponieważ w interfejsie mogą być // tylko publiczne metody abstrakcyjne, // specyfikatory public i abstract niepotrzebne a jego implementacja w przykładowej klasie Wodospad: public class Wodospad implements Speakable { @Override public String getvoice(int voice) { // metody interfejsu muszą być zdefiniowane jako publiczne! if (voice == LOUD) return "SZSZSZSZSZSZ..."; else if (voice == QUIET) return "szszszszszsz..."; else return "?"; 5
Interfejsy - przykład 6 Rozważmy teraz interfejs, opisujący obiekty zdolne się poruszać: public interface Moveable { void start(); void stop(); Zauważmy, że ta funkcjonalność dotyczy zarówno Psa, jak i pojazdów np. Samochodu. Zdefiniujmy ponownie klasę Zwierz jako abstrakcyjną: public abstract class Zwierz { private String name = "bez imienia"; public Zwierz() { public Zwierz(String name) { this.name = name; public abstract String gettyp(); public String getname() { return name;
Interfejsy - przykład public class Pies extends Zwierz implements Speakable, Moveable { public Pies() { public Pies(String name) { super(name); @Override //nadpisujemy metodę z klasy Zwierz public String gettyp() { return "Pies"; @Override //implementujemy metodę z interfejsu Speakable public String getvoice(int voice) { if (voice == LOUD) return "HAU... HAU... HAU... "; else return "hau... hau..."; @Override //implementujemy metodę z interfejsu Moveable public void start() { System.out.println("Pies " + getname() + " biegnie"); @Override //implementujemy metodę z interfejsu Moveable public void stop() { System.out.println("Pies " + getname() + " stanął"); 7
Interfejsy - przykład 8 public class TestInterfaces { public static void main(string[] args) { Pies kuba = new Pies("Kuba"); kuba.start(); System.out.println(kuba.getVoice(Speakable.LOUD)); kuba.stop(); Wyjście: Pies Kuba biegnie HAU... HAU... HAU... Pies Kuba stanął Zmienna kuba jest typu Pies, a to znaczy, że jest również typu Zwierz oraz typu Speakable i Moveable. Dzięki koncepcji interfejsów uzyskaliśmy poszerzenie polimorfizmu. Polimorficzne odwołania są teraz możliwe nie tylko wzdłuż hierarchii dziedziczenia klas, ale również w poprzek.
Interfejsy przykład polimorfizmu 9 public class TestInterfaces { public static void rozmowa(speakable... rozmowcy) { for (Speakable rozmowca : rozmowcy) { if (rozmowca instanceof Zwierz) System.out.print(((Zwierz)rozmowca).getTyp() + ": "); System.out.println(rozmowca.getVoice(Speakable.LOUD)); public static void main(string[] args) { System.out.println("Rozmowa:"); rozmowa(new Pies("Burek"), new Wodospad(), new Pies("Azor")); Wynik: Rozmowa: Pies: HAU... HAU... HAU... SZSZSZSZSZSZ... Pies: HAU... HAU... HAU...
10 Interfejsy metody domyślne W Javie 8 wprowadzono możliwość definiowania metod domyślnych poprzedzonych słowem kluczowym default. W takim przypadku w interfejsie zawarta jest implementacja tych metod. Klasy implementujące ten interfejs mogą, ale nie muszą przedefiniowywać takie metody, chyba że klasa implementuje dwa lub więcej interfejsów zawierających metodę o identycznej sygnaturze. Wówczas musi ona zostać przedefiniowana w klasie. Przykład: W interfejsie Speakable zdefiniujmy domyślną metodę bezparametrową getvoice(): public interface Speakable { int QUIET = 0; // <- publiczne stałe statyczne int LOUD = 1; // domyślnie public static final String getvoice(int voice); // <- metoda abstrakcyjna; default String getvoice(){ return getvoice(quiet); Nie zmieniając nic w klasie Pies, dla obiektu tej klasy można np. Pies kuba = new Pies("Kuba"); System.out.println(kuba.getVoice()); //hau... hau...
Interfejsy metody domyślne 11 Kolejny przykład metoda domyślna foreach z interfejsu Iterable // (stripped down version of the real Iterable.java) public interface Iterable<T> { Iterator<T> iterator(); // As in prior versions // example of a new JDK 8 default method default void foreach(consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { Przykład: action.accept(t); String[] names={"ala", "Ola", "Ula"; Stream.of(names).forEach(name -> System.out.println(name)); Metody domyślne umożliwiają prostą rozbudowę interfejsów. Dzięki ich wprowadzeniu można było rozbudować wybrane interfejsy Javy, tak by mogły być używane z nowo wprowadzonymi elementami programowania funkcyjnego.
Interfejsy metody domyślne - dziedziczenie 12 DZIEDZICZENIE METOD DOMYŚLNYCH public interface A { default int foo(integer n) { return n + n; public interface B { default int foo(integer n) { return n + 2; public class C implements A,B { @Override public int foo(integer n) { return A.super.foo(n);
Interfejsy funkcyjne 13 INTERFEJSY FUNKCYJNE to interfejsy posiadające dokładnie jedną metodę abstrakcyjną (mogą posiadać dowolnie wiele metod default) @FunctionalInterface public interface Command<T,V> { V execute(t parameter); default void itmaycontainsdefaultmethod() { //... default Long andanotherdefaultmethod() { return 20L; Adnotacja @FunctionalInterface - opcjonalnie
Metody statyczne w interfejsach 14 Jak wspominaliśmy wcześniej interfejsy oprócz metod abstrakcyjnych i metod domyślnych mogą zawierać również metody statyczne: public interface InterfaceWithStaticMethod { public static String itworks(int number) { return String.format("Works for %d", number); Uwaga: Znak % -znacznik formatowania, np.: double[] arr = { 23.59004, 35.7, 3.0, 9; for ( double el : arr ) { System.out.format( "%.2f ", el ) ;//2-miejsca po przecinku output: 23,59 35,70 3,00 9,00
Metody prywatne w interfejsach Java 9 15 Od Java 9 w interfejsach mogą występować: stałe (constant variables) metody abstrakcyjne (abstract methods) metody domyślne (default methods) metody statyczne (static methods) metody prywatne (private methods) prywatne metody statyczne (private static methods) Prywatne metody i prywatne metody statyczne zostały dodane do interfejsów w Java 9. Zatem od Java 9 możemy umieszczać metody prywatne w interejsach używając modyfikatora private (tak jak w przypadku zwykłych metod prywatnych). W metodach tych możemy umieszczać np. powtarzający się w innych metodach kod. Ponadto umożliwiają one ukrycie części kodu przed odbiorcą. Oczywiście metody private nie mogą być jednocześnie abstract. Metody te muszą być zaimplementowane (mieć swoje ciało), gdyż klasy potomne ich nie dziedziczą ( więc nie mogą w nich być zaimplementowane).
Metody prywatne w interfejsach Java 9 interface Calc{ default int addnaturalnumbers(int... nums){ return add(nums); default int addevennumbers(int... nums){ return add(nums); default int addoddnumbers(int... nums){ return add(nums); private int add(int...nums){//w funkcji private powtarzający się kod int sum = 0; for(int i:nums){ sum = sum+i; return sum; public class MyCalc implements Calc{ public static void main(string[] args){ MyCalc mycalc = new MyCalc(); int sumofnaturalnumbers = mycalc.addnaturalnumbers(1,2,3,4,5); System.out.println(sumOfNaturalNumbers); int sumofevens = mycalc.addevennumbers(2,4,6); System.out.println(sumOfEvens); int sumofodds = mycalc.addoddnumbers(1,3,5); System.out.println(sumOfOdds); 16
17 public interface DBLogging { String MONGO_DB_NAME = "ABC_Mongo_Datastore"; String NEO4J_DB_NAME = "ABC_Neo4J_Datastore"; String CASSANDRA_DB_NAME = "ABC_Cassandra_Datastore"; Metody prywatne w interfejsach Java 9 default void loginfo(string message) { log(message, "INFO"); default void logwarn(string message) { log(message, "WARN"); default void logerror(string message) { log(message, "ERROR"); default void logfatal(string message) { log(message, "FATAL"); private void log(string message, String msgprefix) { // Step 1: Connect to DataStore // Step 2: Log Message with Prefix and styles etc. // Step 3: Close the DataStore connection // Any other abstract, static, default methods
Mixin 18 Dzięki metodom domyślnym staje się możliwe wielodziedziczenie implementacji metod- mixin. MIXIN oznacza kombinację metod z różnych klas, która może być dodana do innej klasy interface Bear{ default String bear(){return "Bear"; interface Cat{ default String cat(){return "Cat"; class Bintorung implements Bear, Cat{ private String name; public Bintorung(String name){this.name=name; public String tostring(){ return " nazywam się" + name+ "\n jestem Bintorung,\n czyli" + bear() + cat();
Mixin 19 Przez implementację dwóch interfejsów w klasie Bintorung uzyskaliśmy mixin domyślnych (zaimplementowanych) metod tych interfejsów, co pozwala na rzecz obiektu Bintorung wywoływać metody: bear() i cat(). Dzięki temu nasz bintorung- zwierze z Azji, znane też jako bearcat, po wykonaniu: Bintorung wayan = new Bintorung("Wayan"); System.out.println(wayan); może o sobie powiedzieć Nazywam się Wayan i jestem Bintorung, czyli BearCat