1. Evner
For bedre at forstå fordelene ved grænseflader, og hvor de skal bruges, er vi nødt til at tale om nogle mere abstrakte ting.
En klasse modellerer normalt et bestemt objekt. En grænseflade svarer mindre til objekter og mere til deres evner eller roller.
For eksempel er ting som biler, cykler, motorcykler og hjul bedst repræsenteret som klasser og objekter. Men deres evner - såsom "Jeg kan rides", "Jeg kan transportere mennesker", "Jeg kan stå" - præsenteres bedre som grænseflader. Her er nogle eksempler:
Kode | Beskrivelse |
---|---|
|
Svarer til evnen til at bevæge sig |
|
Svarer til evnen til at blive redet |
|
Svarer til evnen til at transportere ting |
|
Klassen Wheel kan bevæge sig |
|
Klassen Car kan bevæge sig, blive redet og transportere ting |
|
Klassen Skateboard kan bevæge sig og rides |
2. Roller
Grænseflader forenkler i høj grad en programmørs liv. Meget ofte har et program tusindvis af objekter, hundredvis af klasser, men kun et par dusin grænseflader , dvs. roller . Der er få roller, men der er mange måder at kombinere dem på (klasser).
Hele pointen er, at du ikke behøver at skrive kode for i hver klasse for at interagere med hver anden klasse. Du skal bare interagere med deres roller (grænseflader).
Forestil dig, at du er en kæledyrstræner. Hvert af de kæledyr, du arbejder med, kan have flere forskellige evner. Du kommer i et venligt skænderi med din nabo om, hvis kæledyr kan larme mest. For at afgøre sagen, opstiller du bare alle de kæledyr, der kan "tale", og du giver dem kommandoen: Tal!
Du er ligeglad med, hvad det er for et dyr, eller hvilke andre evner de har. Også selvom de kan lave en tredobbelt saltomortale. I dette øjeblik er du kun interesseret i deres evne til at tale højt. Sådan ser det ud i kode:
Kode | Beskrivelse |
---|---|
|
Evnen CanSpeak . Denne grænseflade forstår kommandoen til speak , hvilket betyder, at den har en tilsvarende metode. |
|
Dyr, der har denne funktion.
For at lette forståelsen gav vi klassernes navne på engelsk. Dette er tilladt i Java, men det er meget uønsket.
|
|
Og hvordan giver vi dem kommandoen? |
Når antallet af klasser i dine programmer når op på tusindvis, vil du ikke kunne leve uden grænseflader. I stedet for at beskrive interaktionen mellem tusindvis af klasser, er det nok at beskrive interaktionen mellem et par dusin grænseflader - dette forenkler livet i høj grad.
Og når det kombineres med polymorfi, er denne tilgang generelt en kæmpe succes.
3. default
Implementering af grænseflademetoder
Abstrakte klasser kan have variabler og implementeringer af metoder, men de kan ikke have multiple arv. Grænseflader kan ikke have variabler eller implementeringer af metoder, men det kan have flere arv.
Situationen er udtrykt i følgende tabel:
Evne/egenskab | Abstrakte klasser | Grænseflader |
---|---|---|
Variabler | ✔ | ✖ |
Metode implementering | ✔ | ✖ |
Multipel arv | ✖ | ✔ |
Så nogle programmører ønskede virkelig, at grænseflader kunne have metodeimplementeringer. Men at have mulighed for at tilføje en metodeimplementering betyder ikke, at en altid vil blive tilføjet. Tilføj det, hvis du vil. Eller hvis du ikke gør det, så lad være.
Derudover skyldes problemer med multipel nedarvning primært variabler. Det var i hvert fald, hvad de besluttede og gjorde. Startende med JDK 8 introducerede Java muligheden for at tilføje metodeimplementeringer til grænseflader.
Her er en opdateret tabel (for JDK 8 og nyere):
Evne/egenskab | Abstrakte klasser | Grænseflader |
---|---|---|
Variabler | ✔ | ✖ |
Metode implementering | ✔ | ✔ |
Multipel arv | ✖ | ✔ |
Nu for abstrakte klasser såvel som grænseflader, kan du erklære metoder med eller uden implementering. Og det er fremragende nyheder!
I abstrakte klasser skal metoder uden implementering indledes med nøgleordet abstract
. Du behøver ikke tilføje noget før metoder med en implementering. I grænseflader er det modsatte tilfældet. Hvis en metode ikke har en implementering, skal der ikke tilføjes noget. Men hvis der er en implementering, så default
skal nøgleordet tilføjes.
For nemheds skyld præsenterer vi disse oplysninger i følgende lille tabel:
Evne/egenskab | Abstrakte klasser | Grænseflader |
---|---|---|
Metoder uden implementering | abstract |
– |
Metoder med en implementering | – | default |
Problem
Brug af grænseflader, der har metoder, kan i høj grad forenkle store klassehierarkier. For eksempel kan abstraktet InputStream
og OutputStream
klasserne erklæres som grænseflader! Dette lader os bruge dem meget oftere og meget mere bekvemt.
Men der er allerede titusinder af millioner (milliarder?) Java-klasser i verden. Og hvis du begynder at ændre standardbiblioteker, så kan du måske ødelægge noget. Som alt! 😛
For ikke ved et uheld at bryde eksisterende programmer og biblioteker blev det besluttet, at metodeimplementeringer i grænseflader ville have den laveste arveprioritet .
For eksempel, hvis en grænseflade arver en anden grænseflade, der har en metode, og den første grænseflade erklærer den samme metode, men uden en implementering, så vil metodeimplementeringen fra den nedarvede grænseflade ikke nå den nedarvede grænseflade. Eksempel:
interface Pet
{
default void meow()
{
System.out.println("Meow");
}
}
interface Cat extends Pet
{
void meow(); // Here we override the default implementation by omitting an implementation
}
class Tom implements Cat
{
}
Koden vil ikke kompilere, fordi Tom
klassen ikke implementerer meow()
metoden.
GO TO FULL VERSION