Ce face Interfețe (interface, implements)
O interfață este un contract: o listă de metode, de obicei fără implementare. O clasă care scrie implements Plata trebuie să ofere toate metodele abstracte ale interfeței, altfel nu compilează (sau trebuie declarată abstract). Metodele din interfață sunt implicit public.
O clasă poate implementa oricâte interfețe (implements A, B, C), deși poate extinde o singură clasă. Din Java 8, interfețele pot avea metode default (cu implementare, moștenite de clase) și metode static; din Java 9 și metode private ajutătoare. Câmpurile dintr-o interfață sunt automat public static final (constante).
Interfața este și un tip: o variabilă Plata p poate ține orice obiect care o implementează. Biblioteca standard folosește peste tot interfețe: List, Comparable, Runnable, AutoCloseable. O interfață cu o singură metodă abstractă este funcțională și poate primi o lambda.
Exemple
Exemplul 1: O interfață, mai multe implementări
Plata cere metoda suma() și oferă gratuit descriere(). PlataRate își rescrie descrierea, iar codul din main lucrează doar cu tipul Plata.
interface Plata {
double suma();
default String descriere() {
return getClass().getSimpleName() + ": " + suma() + " lei";
}
}
class PlataCard implements Plata {
final double valoare;
PlataCard(double valoare) { this.valoare = valoare; }
@Override
public double suma() { return valoare; }
}
class PlataRate implements Plata {
final double rata;
final int luni;
PlataRate(double rata, int luni) { this.rata = rata; this.luni = luni; }
@Override
public double suma() { return rata * luni; }
@Override
public String descriere() {
return "Rate: " + luni + " x " + rata + " = " + suma() + " lei";
}
}
public class Main {
public static void main(String[] args) {
Plata[] plati = {new PlataCard(249.99), new PlataRate(150, 12)};
double total = 0;
for (Plata p : plati) {
System.out.println(p.descriere());
total += p.suma();
}
System.out.println("Total: " + total + " lei");
}
}
Rezultat
PlataCard: 249.99 lei
Rate: 12 x 150.0 = 1800.0 lei
Total: 2049.99 lei
Exemplul 2: O clasă care implementează două interfețe
Termostat este în același timp Pornibil și Masurabil. Constanta din interfață este static final, iar metoda static se apelează pe numele interfeței.
interface Pornibil {
void porneste();
void opreste();
}
interface Masurabil {
double PRAG = 18.0; // implicit public static final
double valoare();
static String unitate() {
return "°C";
}
}
class Termostat implements Pornibil, Masurabil {
private boolean pornit;
private final double temperatura;
Termostat(double temperatura) { this.temperatura = temperatura; }
@Override public void porneste() { pornit = true; }
@Override public void opreste() { pornit = false; }
@Override public double valoare() { return temperatura; }
boolean pornit() { return pornit; }
}
public class Main {
public static void main(String[] args) {
Termostat t = new Termostat(16.5);
if (t.valoare() < Masurabil.PRAG) {
t.porneste();
}
System.out.println("temperatură: " + t.valoare() + Masurabil.unitate());
System.out.println("centrala pornită: " + t.pornit());
Pornibil p = t;
p.opreste();
System.out.println("după oprire: " + t.pornit());
}
}
Rezultat
temperatură: 16.5°C
centrala pornită: true
după oprire: false
Exemplul 3: Interfețe din biblioteca standard: Comparable
Implementând Comparable<Produs>, produsele capătă o ordine naturală (după preț), iar Arrays.sort știe să le sorteze.
import java.util.Arrays;
class Produs implements Comparable<Produs> {
final String nume;
final double pret;
Produs(String nume, double pret) {
this.nume = nume;
this.pret = pret;
}
@Override
public int compareTo(Produs altul) {
return Double.compare(pret, altul.pret);
}
@Override
public String toString() {
return nume + " (" + pret + " lei)";
}
}
public class Main {
public static void main(String[] args) {
Produs[] cos = {
new Produs("cafea", 39.9),
new Produs("zahăr", 6.5),
new Produs("ceai", 12.0),
};
Arrays.sort(cos);
System.out.println(Arrays.toString(cos));
}
}
Rezultat
[zahăr (6.5 lei), ceai (12.0 lei), cafea (39.9 lei)]
Greșeli frecvente
- Implementezi metoda din interfață fără
public:attempting to assign weaker access privileges; was public. Metodele de interfață sunt publice, deci și implementările trebuie să fie. - Uiți o metodă abstractă:
Termostat is not abstract and does not override abstract method valoare() in Masurabil. - Scrii
extendsîn loc deimplementspentru o interfață în declarația unei clase (sau invers). Clasele implementează interfețe; o interfață extinde alte interfețe.
Comenzi înrudite
Clase abstracte (abstract): O clasă abstract nu poate fi instanțiată direct; poate avea metode abstracte, pe care subclasele concrete sunt obligate să le scrie.Polimorfism: Polimorfismul înseamnă că același apel (f.arie()) execută metoda clasei reale a obiectului, chiar dacă variabila are tipul superclasei.Expresii lambda: O lambda este o funcție scurtă, fără nume, scrisă ca parametri -> corp, folosită oriunde se așteaptă o interfață funcțională.Moștenire (extends): Cu extends o clasă moștenește câmpurile și metodele altei clase și le poate extinde sau rescrie; Java permite o singură superclasă.sealed, permits și non-sealed: O clasă sau interfață sealed își alege explicit subtipurile (permits); fiecare subtip este final, sealed sau non-sealed.