Podstawowym elementem składowym aplikacji zaimplementowanych w języku Java są klasy. Podstawowym, ale nie jedynym. Są jeszcze interfejsy i typy wyliczeniowe. O typach wyliczeniowych powiemy sobie nieco później, teraz skupmy się na interfejsach.
Klasy są definicją typów. Typów, jakie mogą mieć obiekty. Interfejsy także są definicją typów, tyle że nieco uboższą. Klasy zawierają zmienne i stałe oraz deklaracje metod i ich implementacje. Interfejsy mogą zawierać tylko stałe i deklaracje metod – bez implementacji. Mówiąc bardziej ogólnie – klasy definiują zarówno „co” mogą robić obiekty, jak i to „jak” to będzie robione; interfejsy definiują tylko „co”, ale bez „jak”.
Klasy deklarujemy z użyciem słówka kluczowego class. Interfejsy bardzo podobnie, tyle że używamy słówka kluczowego interface. Ogólna postać deklaracji interfejsu poniżej:
{modyfikatory interfejsu} interface {nazwa interfejsu} { {deklaracje stałych i metod} }
Wróćmy do naszego przykładu sklepu internetowego. Moglibyśmy zdefiniować interfejs definiujący produkt o nazwie Product. Definiując interfejs opisujący produkt opisujemy de facto cechy czy umiejętności jakie posiada każdy produkt. W języku Java oznacza to potrzebę zdefiniowania odpowiednich metod. Każdy produkt ma cenę, więc możemy w klasie Product zdefiniować metodę getPrice() która tą cenę zwróci. Każdy produkt ma również jakiś opis. Zdefiniujmy więc metodę getDesc() która ten opis zwróci jako obiekt klasy String. Nasz interfejs powinien wyglądać następująco:
public interface Product { double getPrice(); String getDesc(); }
W artykule „Dziedziczenie” implementowaliśmy produkt jako klasę, z której z kolei dziedziczyła klasa opisująca książkę. Tym razem produkt zrobiliśmy interfejsem, więc można by postawić pytanie – jak zaimplementujemy teraz klasę opisującą książkę? Otóż klasa opisująca książkę powinna implementować (!) interfejs opisujący produkt.
Co to znaczy zaimplementować interfejs? Otóż interfejs to nic innego, jak tylko zbiór deklaracji metod, zatem zaimplementować interfejs to znaczy zaimplementować wszystkie zadeklarowane w nim metody. Aby powiedzieć, że jedna klasa dziedziczy z innej klasy używaliśmy słówka kluczowego extends. Aby powiedzieć, że klasa implementuje interfejs używamy słówka kluczowego implements. Klasa Book implementująca interfejs Product poniżej:
public class Book implements Product { private double price; private String title; public Book(String title, double price) { this.title = title; this.price = price; } public double getPrice() { // metoda z interfejsu Product return price; } public String getDesc() { // metoda z interfejsu Product return "Książka pt. '" + title + "'"; } public String getTitle() { return title; } }
Jak widać w klasie Book zaimplementowano wszystkie metody zdefiniowane w interfejsie Product oraz dodatkowo metodę getTitle(). To, że klasa implementuje interfejs nakłada na nią obowiązek implementacji wszystkich zadeklarowanych tam metod, natomiast nie ogranicza w żaden sposób możliwości implementacji metod dodatkowych.
Interfejs jest definicją typu, wobec tego możemy korzystając z polimorfizmu zadeklarować zmienną typu Product (produkt jest teraz interfejsem) i przypisać do tej zmiennej instancję zdefiniowanej powyżej klasy Book (bo klasa Book implementuje interfejs Product). Instancji interfejsów nie możemy tworzyć. Interfejsy służą tylko do tego, żeby zdefiniować wymagania dla implementujących je klas.