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();
}
  1. I stedet for nøgleordet classskriver vi interface.
  2. Det indeholder kun abstrakte metoder (skriv ikke nøgleordet abstract)
  3. Faktisk har grænseflader allepublic metoder
2. Interface arv

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 implementssø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 ChessItemindeholder klassen én abstrakt metode - draw().

Den tekniske betydning af søgeordene extendsog implementser 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
class Student
{
   private String name;
   public Student(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return this.name;
   }

   private void setName(String name)
   {
      this.name = name;
   }
interface Student
{
   public String getName();
}

class StudentImpl implements Student
{
   private String name;
   public StudentImpl(String name)
   {
      this.name = name;
   }

   public String getName()
   {
      return this.name;
   }

   private void setName(String name)
   {
      this.name = name;
   }
}
public static void main(String[] args)
{
   Student student = new Student("Alibaba");
   System.out.println(student.getName());
}
public static void main(String[] args)
{
   Student student = new StudentImpl("Ali")
   System.out.println(student.getName());
}

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 LinkedLister der to forskellige implementeringer af Listgræ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:

Multipel arv

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 valuevariablen. Dens XCoordinate efterkommerklasse bruger denne variabel til at gemme værdien x, og YCoordinateefterkommerklassen bruger den til at gemme yværdien.

Og det virker. Separat. Men hvis vi ønsker, at XYCoordinates-klassen skal arve både klasserne XCoordinateog YCoordinateklasserne, 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.