1. Evner
For bedre å forstå fordelene med grensesnitt og hvor de skal brukes, må vi snakke om noen mer abstrakte ting.
En klasse modellerer vanligvis et bestemt objekt. Et grensesnitt tilsvarer mindre objekter, og mer til deres evner eller roller.
For eksempel er ting som biler, sykler, motorsykler og hjul best representert som klasser og objekter. Men deres evner - som "Jeg kan bli ridd", "Jeg kan transportere folk", "Jeg kan stå" - presenteres bedre som grensesnitt. Her er noen eksempler:
Kode | Beskrivelse |
---|---|
|
Tilsvarer evnen til å bevege seg |
|
Tilsvarer evnen til å bli ridd |
|
Tilsvarer evnen til å transportere ting |
|
Klassen Wheel kan bevege seg |
|
Klassen Car kan bevege seg, bli ridd og transportere ting |
|
Klassen Skateboard kan bevege seg og bli ridd |
2. Roller
Grensesnitt forenkler en programmerers liv. Svært ofte har et program tusenvis av objekter, hundrevis av klasser, men bare et par dusin grensesnitt , dvs. roller . Det er få roller, men det er mange måter å kombinere dem på (klasser).
Hele poenget er at du ikke trenger å skrive kode for i hver klasse for å samhandle med annenhver klasse. Du trenger bare å samhandle med rollene deres (grensesnitt).
Tenk deg at du er en kjæledyrtrener. Hvert av kjæledyrene du jobber med kan ha flere forskjellige evner. Du kommer i en vennlig krangel med naboen din om hvem sine kjæledyr som kan lage mest støy. For å avgjøre saken, stiller du bare opp alle kjæledyrene som kan "snakke", og du gir dem kommandoen: Snakk!
Du bryr deg ikke om hva slags dyr de er eller hvilke andre evner de har. Selv om de kan gjøre en trippel ryggsalto. I dette øyeblikket er du bare interessert i deres evne til å snakke høyt. Slik vil det se ut i kode:
Kode | Beskrivelse |
---|---|
|
Evnen CanSpeak . Dette grensesnittet forstår kommandoen til speak , noe som betyr at det har en tilsvarende metode. |
|
Dyr som har denne funksjonen.
For å lette forståelsen ga vi klassenavnene på engelsk. Dette er tillatt i Java, men det er svært uønsket.
|
|
Og hvordan gir vi dem kommandoen? |
Når antallet klasser i programmene dine når tusenvis, vil du ikke kunne leve uten grensesnitt. I stedet for å beskrive samspillet mellom tusenvis av klasser, er det nok å beskrive samspillet til noen få dusin grensesnitt - dette forenkler livet i stor grad.
Og når den kombineres med polymorfisme, er denne tilnærmingen generelt en knusende suksess.
3. default
Implementeringen av grensesnittmetoder
Abstrakte klasser kan ha variabler og implementeringer av metoder, men de kan ikke ha multippel arv. Grensesnitt kan ikke ha variabler eller implementeringer av metoder, men som kan ha flere arv.
Situasjonen er uttrykt i følgende tabell:
Evne/egenskap | Abstrakte klasser | Grensesnitt |
---|---|---|
Variabler | ✔ | ✖ |
Metodeimplementering | ✔ | ✖ |
Multippel arv | ✖ | ✔ |
Så, noen programmerere ønsket virkelig at grensesnitt skulle ha muligheten til å ha metodeimplementeringer. Men å ha muligheten til å legge til en metodeimplementering betyr ikke at en alltid vil bli lagt til. Legg den til hvis du vil. Eller hvis du ikke gjør det, så ikke gjør det.
I tillegg skyldes problemer med multippel arv først og fremst variabler. I alle fall var det det de bestemte og gjorde. Fra og med JDK 8 introduserte Java muligheten til å legge til metodeimplementeringer til grensesnitt.
Her er en oppdatert tabell (for JDK 8 og nyere):
Evne/egenskap | Abstrakte klasser | Grensesnitt |
---|---|---|
Variabler | ✔ | ✖ |
Metodeimplementering | ✔ | ✔ |
Multippel arv | ✖ | ✔ |
Nå for abstrakte klasser så vel som grensesnitt, kan du deklarere metoder med eller uten implementering. Og dette er gode nyheter!
I abstrakte klasser må metoder uten implementering innledes med nøkkelordet abstract
. Du trenger ikke legge til noe før metoder med en implementering. I grensesnitt er det motsatte. Hvis en metode ikke har en implementering, bør ingenting legges til. Men hvis det er en implementering, default
må søkeordet legges til.
For enkelhets skyld presenterer vi denne informasjonen i følgende lille tabell:
Evne/egenskap | Abstrakte klasser | Grensesnitt |
---|---|---|
Metoder uten implementering | abstract |
– |
Metoder med en implementering | – | default |
Problem
Å bruke grensesnitt som har metoder kan i stor grad forenkle store klassehierarkier. For eksempel kan abstraktet InputStream
og OutputStream
klassene erklæres som grensesnitt! Dette lar oss bruke dem mye oftere og mye mer praktisk.
Men det finnes allerede titalls millioner (milliarder?) Java-klasser i verden. Og hvis du begynner å endre standardbiblioteker, kan du ødelegge noe. Som alt! 😛
For ikke å ødelegge eksisterende programmer og biblioteker ved et uhell, ble det bestemt at metodeimplementeringer i grensesnitt ville ha den laveste arveprioriteten .
For eksempel, hvis ett grensesnitt arver et annet grensesnitt som har en metode, og det første grensesnittet erklærer den samme metoden, men uten en implementering, vil ikke metodeimplementeringen fra det arvede grensesnittet nå det arvede grensesnittet. 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