1. Introduserer grensesnitt
I dag er din dag for kunnskap. Et annet nytt og interessant emne er grensesnitt.
Konseptet med et grensesnitt er barnet til prinsippene for abstraksjon og polymorfisme. Et grensesnitt ligner veldig på en abstrakt klasse, der alle metodene er abstrakte. Det er deklarert på samme måte som en klasse, men vi bruker nøkkelordet interface
.
interface Feline
{
void purr();
void meow();
void growl();
}
Her er noen nyttige fakta om grensesnitt:
1. Erklære et grensesnitt
interface Drawable
{
void draw();
}
interface HasValue
{
int getValue();
}
- I stedet for nøkkelordet
class
skriver viinterface
. - Den inneholder bare abstrakte metoder (ikke skriv nøkkelordet
abstract
) - Faktisk har grensesnitt alle
public
metoder
Et grensesnitt kan bare arve grensesnitt. Men et grensesnitt kan ha mange foreldre. En annen måte å si dette på er å si at Java har flere grensesnittarv. Eksempler:
interface Piece extends Drawable, HasValue
{
int getX();
int getY();
}
3. Arve klasser fra grensesnitt
En klasse kan arve flere grensesnitt (bare fra én klasse). Dette gjøres ved å bruke implements
nøkkelordet. Eksempel:
abstract class ChessItem implements Drawable, HasValue
{
private int x, y, value;
public int getValue()
{
return value;
}
public int getX()
{
return x;
}
public int getY()
{
return y;
}
}
ChessItem-klassen er erklært abstrakt: den implementerer alle de nedarvede metodene unntatt draw
. Med andre ord ChessItem
inneholder klassen én abstrakt metode - draw()
.
Den tekniske betydningen av søkeordene extends
og implements
er den samme: begge er arv. Skillet ble gjort for å forbedre lesbarheten til koden. Vi sier også at klasser er arvet (via extends
) og grensesnitt er implementert (via implements
)
4. Variabler
Her er det viktigste: vanlige variabler kan ikke deklareres i grensesnitt (selv om statiske kan).
Men hvorfor trenger vi grensesnitt? Når brukes de? Grensesnitt har to sterke fordeler fremfor klasser:
2. Å skille "beskrivelsen av metoder" fra implementeringen av dem.
Tidligere sa vi at hvis du vil tillate at metodene til klassen din kalles fra andre klasser, må metodene dine merkes med nøkkelordet public
. Hvis du vil at noen av disse metodene bare skal kalles fra klassen din, må du merke dem med nøkkelordet private
. Med andre ord deler vi klassens metoder inn i to kategorier: "for alle å bruke" og "bare til eget bruk".
Grensesnitt bidrar til å styrke denne divisjonen ytterligere. Vi vil lage en spesiell "klasse for alle å bruke" så vel som en andre klasse "kun for eget bruk", som vil arve den første klassen. Her er omtrent hvordan det vil se ut:
Før | Etter |
---|---|
|
|
|
|
Vi deler klassen vår i to: et grensesnitt og en klasse som arver grensesnittet . Og hva er fordelen her?
Mange forskjellige klasser kan implementere (arve) det samme grensesnittet. Og hver enkelt kan ha sin egen oppførsel. For eksempel ArrayList
LinkedList
er det to forskjellige implementeringer av List
grensesnittet.
Dermed skjuler vi ikke bare de ulike implementeringene, men også selve implementeringsklassen (siden vi bare trenger grensesnittet i koden). Dette lar oss være veldig fleksible: akkurat når programmet kjører, kan vi erstatte ett objekt med et annet, endre et objekts oppførsel uten å påvirke alle klassene som bruker det.
Dette er en veldig kraftig teknikk når den kombineres med polymorfisme. Foreløpig er det langt fra åpenbart hvorfor du bør gjøre dette. Du må først møte programmer med dusinvis eller hundrevis av klasser for å forstå at grensesnitt kan gjøre livet ditt så mye enklere enn uten dem.
3. Multippel arv
I Java kan alle klasser bare ha én overordnet klasse. I andre programmeringsspråk kan klasser ofte ha flere overordnede klasser. Dette er veldig praktisk, men gir også mange problemer.
Javas skapere kom frem til et kompromiss: de forbød multippel arv av klasser, men tillot multippel arv av grensesnitt. Et grensesnitt kan ha flere overordnede grensesnitt. En klasse kan ha flere overordnede grensesnitt, men bare én overordnet klasse.
Hvorfor forbød de multippel arv av klasser, men tillot multippel nedarving av grensesnitt? På grunn av såkalt diamantarvsproblem:
Når B-klassen arver A-klassen, vet den ikke noe om C- og D-klassene. Så den bruker variablene til A-klassen slik den finner passende. C-klassen gjør det samme: den bruker variablene til A-klassen, men på en annen måte. Og alt dette resulterer i en konflikt i D-klassen.
La oss se på følgende enkle eksempel. La oss si at vi har 3 klasser:
class Data
{
protected int value;
}
class XCoordinate extends Data
{
public void setX (int x) { value = x;}
public int getX () { return value;}
}
class YCoordinate extends Data
{
public void setY (int y) { value = y;}
public int getY () { return value; }
}
Data-klassen lagrer variabelen value
. Dens XCoordinate etterkommerklasse bruker den variabelen til å lagre x
verdien, og YCoordinate
etterkommerklassen bruker den til å lagre y
verdien.
Og det fungerer. Hver for seg. Men hvis vi vil at XYCoordinates-klassen skal arve både XCoordinate
og YCoordinate
klassene, får vi ødelagt kode. Denne klassen vil ha metodene til sine stamfarklasser, men de vil ikke fungere riktig, fordi de har den samme value variable
.
Men fordi grensesnitt ikke kan ha variabler, kan de ikke ha denne typen konflikt. Følgelig er multippel arv av grensesnitt tillatt.
GO TO FULL VERSION