1. Introduktion af grænseflader
I dag er din dag for viden. Et andet nyt og interessant emne er grænseflader.
Begrebet en grænseflade er barnet af principperne om abstraktion og polymorfi. En grænseflade minder meget om en abstrakt klasse, hvor alle metoderne er abstrakte. Det er deklareret på samme måde som en klasse, men vi bruger søgeordet interface
.
interface Feline
{
void purr();
void meow();
void growl();
}
Her er nogle nyttige fakta om grænseflader:
1. Erklæring af en grænseflade
interface Drawable
{
void draw();
}
interface HasValue
{
int getValue();
}
- I stedet for nøgleordet
class
skriver viinterface
. - Det indeholder kun abstrakte metoder (skriv ikke nøgleordet
abstract
) - Faktisk har grænseflader alle
public
metoder
En grænseflade kan kun arve grænseflader. Men en grænseflade kan have mange forældre. En anden måde at sige dette på er at sige, at Java har flere grænseflader. Eksempler:
interface Piece extends Drawable, HasValue
{
int getX();
int getY();
}
3. Nedarvning af klasser fra grænseflader
En klasse kan arve flere grænseflader (kun fra én klasse). Dette gøres ved hjælp af implements
søgeordet. 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æret abstrakt: den implementerer alle de nedarvede metoder undtagen draw
. Med andre ord ChessItem
indeholder klassen én abstrakt metode - draw()
.
Den tekniske betydning af søgeordene extends
og implements
er den samme: begge er arv. Sondringen blev lavet for at forbedre kodens læsbarhed. Vi siger også, at klasser nedarves (via extends
) og grænseflader implementeres (via implements
)
4. Variabler
Her er det vigtigste: almindelige variabler kan ikke erklæres i grænseflader (selvom statiske kan).
Men hvorfor har vi brug for grænseflader? Hvornår bruges de? Grænseflader har to stærke fordele i forhold til klasser:
2. Adskillelse af "beskrivelse af metoder" fra deres implementering.
Tidligere sagde vi, at hvis du vil tillade, at din klasses metoder kaldes fra andre klasser, så skal dine metoder markeres med nøgleordet public
. Hvis du ønsker, at nogle af disse metoder kun skal kaldes fra din klasse, skal du markere dem med nøgleordet private
. Med andre ord opdeler vi klassens metoder i to kategorier: "til brug for alle" og "kun til eget brug".
Grænseflader er med til at styrke denne opdeling yderligere. Vi laver en særlig "klasse for alle at bruge" samt en anden klasse "kun til eget brug", som vil arve den første klasse. Sådan ser det nogenlunde ud:
Før | Efter |
---|---|
|
|
|
|
Vi deler vores klasse op i to: en grænseflade og en klasse , der arver grænsefladen . Og hvad er fordelen her?
Mange forskellige klasser kan implementere (arve) den samme grænseflade. Og hver enkelt kan have sin egen adfærd. For eksempel ArrayList
LinkedList
er der to forskellige implementeringer af List
grænsefladen.
Således skjuler vi ikke kun de forskellige implementeringer, men også selve implementeringsklassen (da vi kun har brug for grænsefladen i koden). Dette lader os være meget fleksible: Lige som programmet kører, kan vi erstatte et objekt med et andet, ændre et objekts adfærd uden at påvirke alle de klasser, der bruger det.
Dette er en meget kraftfuld teknik, når den kombineres med polymorfi. For nu er det langt fra indlysende, hvorfor du skal gøre dette. Du skal først støde på programmer med snesevis eller hundredvis af klasser for at forstå, at grænseflader kan gøre dit liv så meget nemmere end uden dem.
3. Multipel arv
I Java kan alle klasser kun have én overordnet klasse. I andre programmeringssprog kan klasser ofte have flere forældreklasser. Dette er meget praktisk, men bringer også mange problemer.
Javas skabere nåede frem til et kompromis: de forbød multipel nedarvning af klasser, men tillod multipel nedarvning af grænseflader. En grænseflade kan have flere overordnede grænseflader. En klasse kan have flere overordnede grænseflader, men kun én overordnet klasse.
Hvorfor forbød de multipel nedarvning af klasser, men tillod multipel nedarvning af grænseflader? På grund af såkaldt diamantarvsproblem:

Når B-klassen arver A-klassen, ved den ikke noget om C- og D-klasserne. Så den bruger variablerne i A-klassen, som den finder passende. C-klassen gør det samme: den bruger variablerne i A-klassen, men på en anden måde. Og alt dette resulterer i en konflikt i D-klassen.
Lad os se på følgende enkle eksempel. Lad os sige, 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 gemmer value
variablen. Dens XCoordinate efterkommerklasse bruger denne variabel til at gemme værdien x
, og YCoordinate
efterkommerklassen bruger den til at gemme y
værdien.
Og det virker. Separat. Men hvis vi ønsker, at XYCoordinates-klassen skal arve både klasserne XCoordinate
og YCoordinate
klasserne, så får vi ødelagt kode. Denne klasse vil have metoderne fra sine forfaderklasser, men de vil ikke fungere korrekt, fordi de har den samme value variable
.
Men fordi grænseflader ikke kan have variabler, kan de ikke have denne form for konflikt. Derfor er multipel nedarvning af grænseflader tilladt.
GO TO FULL VERSION