W dzisiejszej lekcji poruszymy kolejny aspekt programowania obiektowego w Javie. Dziś przedstawię wam koncept Interfejsów w Javie, o co w tym wszystkim chodzi, jak możemy je wykorzystać, oraz dlaczego interfejsy są takie ważne! Zapraszam!
01. Czym jest Interfejs w Javie?
Interfejs w pewnym sensie jest podobny do klasy (o klasach możesz przeczytać w tym wpisie). Z pewnymi dość istotnymi różnicami 😉 Jak to zwykle bywa diabeł tkwi w szczegółach 😛
O Interfejsie możemy myśleć jak o kolekcji abstrakcyjnych metod, które muszą zostać zaimplementowane przez klasę która implementuje dany interfejs.
Powyższe zdanie mogło zabrzmieć trochę strasznie, lub może wydawać się skomplikowane, w rzeczywistości jednak jest to bardzo proste, najłatwiej będzie to zaprezentować na prostym przykładnie, stwórzmy interfejs który pozwoli nam obliczać pole powierzchni:
public interface Shape { double calculateArea(); }
Zauważ że występuje tutaj słowo kluczowe interface! Jest to podstawowe odróżnienie klasy od interfejsu. Jak łatwo zauważyć, metoda którą utworzyliśmy w naszym interfejsie, posiada jedynie deklarację, bez żadnego ciała. Jest to metoda abstrakcyjna, a jej „ciało” zostanie stworzone w klasie która będzie implementować nasz interfejs.
Teraz utworzymy klasę, która będzie implementować nasz interfejs:
public class Triangle implements Shape { @Override public double calculateArea() { return 0; } }
Klasa która implementuje interfejs, musi posiadać implementację wszystkich metod zadeklarowanych w interfejsie, w naszym przypadku jest to jedna metoda calculateArea. Nasze IDE zawsze nam podpowie o tym że musimy napisać implementacje dla konkretnych metod. W tym momencie mamy pusty szkielet naszej metody.
Aby wyliczyć pole trójkąta musimy znać wysokość, oraz długość podstawy (tak tak, to tylko jedna z metody wyliczenia pola powierzchni w trójkącie 😉). Tworzymy więc odpowiednia pola w naszej klasie, oraz konstruktor:
public class Triangle implements Shape { private double baseLength; private double heightLength; public Triangle(double baseLength, double heightLength) { this.baseLength = baseLength; this.heightLength = heightLength; } @Override public double calculateArea() { return 0; } }
Nasza klasa posiada dwa pola, długość podstawy, oraz wysokość, klasa posiada również konstruktor który przyjmuje oba te parametry. Czas uzupełnić naszą metodę calculateArea. Cały kod naszej klasy w tym momencie wygląda tak:
public class Triangle implements Shape { private double baseLength; private double heightLength; public Triangle(double baseLength, double heightLength) { this.baseLength = baseLength; this.heightLength = heightLength; } @Override public double calculateArea() { System.out.println("Calculate area of Triangle"); return 0.5*(baseLength*heightLength); } }
Dodaliśmy wypisanie informacji na konsolę, oraz implementację metody która wyliczy nam pole powierzchni dla trójkąta. A teraz użyjemy naszej klasy, w metodzie main zrobimy tak:
Triangle triangle = new Triangle(5,3); System.out.println(triangle.calculateArea());
Na konsoli zostanie wypisana informacja że jest liczona powierzchnia dla trójkąta, oraz zostanie wypisana wyliczona wartość pola powierzchni 😊
Okej, ale po co nam tak naprawdę ten interfejs? Przecież mogliśmy to wszystko zrobić bez żadnego interfejsu! Magia dopiero się zaczyna! 😊 Zróbmy kolejną klasę która będzie implementować nasz interfejs:
public class Square implements Shape { private double a; public Square(double a) { this.a = a; } @Override public double calculateArea() { System.out.println("Calculate area of Square"); return a*a; } }
A teraz użyjmy jej w naszej klasie mian:
public static void main(String[] args) { Triangle triangle = new Triangle(5,3); System.out.println(triangle.calculateArea()); Square square = new Square(12); System.out.println(square.calculateArea()); }
Jeśli teraz pomyślałeś coś w stylu „Okej, mamy dwie klasy które implementują nasz interfejs, ale co z tą magią? Ciągle ten sam efekt mogliśmy osiągnąć gdyby te klasy po prostu miały taką metodę bez żadnych interfejsów!”
Miałbyś 100% racji! A teraz przejdziemy do sedna całej zabawy! Tworzymy metodę która jako parametr przyjmie nasz interfejs a nie jego konkretną implementację!
public static void calculateShapeArea(Shape shape) { System.out.println(shape.calculateArea()); }
A nasza metoda main może teraz wyglądać tak:
public static void main(String[] args) { Triangle triangle = new Triangle(5,3); calculateShapeArea(triangle); Square square = new Square(12); calculateShapeArea(square); } public static void calculateShapeArea(Shape shape) { System.out.println(shape.calculateArea()); }
Metoda tak jak wcześniej powiedzieliśmy, jako parametr przyjmuje interfejs (w naszym przypadku jest to interfejs Shape), a nie jakąś konkretną jego implementację (Triangle lub Square). Metoda nie wie tak naprawdę co konkretnego do niej przekazaliśmy, wie natomiast że co by to nie było, jest to coś co implementuje interfejs Shape, więc może wywołać metodę która jest w nim zadeklarowana!
Inny przykład, ostatnio pisałem o tablicach oraz pętlach. Użyjmy ich wraz z naszym interfejsem:
public static void main(String[] args) { Triangle triangle = new Triangle(5,3); Square square = new Square(12); Shape[] array = {triangle, square}; for (Shape shape: array) { System.out.println(shape.calculateArea()); } }
Tak jak widzisz, stworzyliśmy tablicę która przechowuje Shape, nasza klasa Triangle jak i Square implementuje Shape, zatem obie te klasy mogą być przechowywane w tej samej tablicy, dzięki czemu możemy przeiterować się po wszystkich elementach danej tablicy i wywołać metodę która znajduje się w interfejsie 😊
02. Podsumowanie
Korzystając z interfejsów możemy stworzyć nieograniczoną liczbę różnych klas które będę posiadać własną implementację danego interfejsu. Interfejs mówi nam o tym iż coś posiada daną funkcjonalność, jak ta funkcjonalność działa wewnątrz zależy już od konkretnej implementacji.
W dzisiejszej lekcji opisałem podstawy oraz koncept związany z Interfejsem w Javie. Jak widzisz Interfejsy dają nam naprawdę ogromne możliwości. W programowaniu obiektowym istnieje cały koncept oparty na tym, że powinniśmy programować z wykorzystaniem interfejsów zamiast konkretnych jego implementacji! (“Program to an interface, not an implementation.”)
Dodatkowo interfejsy w Javie przeszły pewne usprawnienia od czasu pierwszych wersji Javy, jednak o tym co zostało dodane porozmawiamy w kolejnej lekcji! 😊