1. Introductie van interfaces

Vandaag is jouw dag voor kennis. Een ander nieuw en interessant onderwerp zijn interfaces.

Het concept van een interface is het kind van de principes van abstractie en polymorfisme. Een interface lijkt erg op een abstracte klasse, waarin alle methoden abstract zijn. Het wordt op dezelfde manier gedeclareerd als een klasse, maar we gebruiken het interfacetrefwoord.

interface Feline
{
   void purr();
   void meow();
   void growl();
}

Hier zijn enkele nuttige feiten over interfaces:

1. Een interface declareren

interface Drawable
{
   void draw();
}

interface HasValue
{
   int getValue();
}
  1. In plaats van het classtrefwoord schrijven we interface.
  2. Het bevat alleen abstracte methoden (schrijf het trefwoord niet op abstract)
  3. In feite hebben interfaces allepublic methoden
2. Overerving van interfaces

Een interface kan alleen interfaces erven. Maar een interface kan veel ouders hebben. Een andere manier om dit te zeggen is door te zeggen dat Java meerdere overerving van interfaces heeft. Voorbeelden:

interface Piece extends Drawable, HasValue
{
   int getX();
   int getY();
}

3. Klassen overnemen van interfaces

Een klasse kan meerdere interfaces erven (slechts van één klasse). Dit gebeurt aan de hand van het implementstrefwoord. Voorbeeld:

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;
   }
}

De klasse ChessItem wordt abstract verklaard: het implementeert alle geërfde methoden behalve draw. Met andere woorden, de ChessItemklasse bevat één abstracte methode - draw().

De technische betekenis van de trefwoorden extendsen implementsis hetzelfde: beide zijn overerving. Het onderscheid is gemaakt om de leesbaarheid van de code te verbeteren. We zeggen ook dat klassen worden geërfd (via extends) en interfaces worden geïmplementeerd (via implements)

4. Variabelen

Dit is het belangrijkste: gewone variabelen kunnen niet worden gedeclareerd in interfaces (hoewel statische variabelen dat wel kunnen).

Maar waarom hebben we interfaces nodig? Wanneer worden ze gebruikt? Interfaces hebben twee sterke voordelen ten opzichte van klassen:



2. Scheiding van de "beschrijving van methoden" van hun implementatie.

Eerder zeiden we dat als je wilt toestaan ​​dat de methoden van je klasse worden aangeroepen vanuit andere klassen, je methoden moeten worden gemarkeerd met het publictrefwoord. Als u wilt dat sommige van die methoden alleen vanuit uw klas worden aangeroepen, moet u ze markeren met het privatetrefwoord. Met andere woorden, we verdelen de methoden van de klasse in twee categorieën: "voor iedereen te gebruiken" en "alleen voor eigen gebruik".

Interfaces helpen deze divisie verder te versterken. We maken een speciale "klasse die iedereen kan gebruiken" en een tweede klasse "alleen voor eigen gebruik", die de eerste klasse zal erven. Hier is ongeveer hoe het eruit zou zien:

Voor Na
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());
}

We hebben onze klasse in tweeën gesplitst: een interface en een klasse die de interface erft . En wat is hier het voordeel?

Veel verschillende klassen kunnen dezelfde interface implementeren (erven). En elk kan zijn eigen gedrag hebben. Er zijn bijvoorbeeld ArrayList LinkedListtwee verschillende implementaties van de Listinterface.

We verbergen dus niet alleen de verschillende implementaties, maar ook de implementatieklasse zelf (aangezien we alleen de interface in de code nodig hebben). Hierdoor zijn we zeer flexibel: terwijl het programma draait, kunnen we het ene object door het andere vervangen, waardoor het gedrag van een object verandert zonder alle klassen die het gebruiken te beïnvloeden.

Dit is een zeer krachtige techniek in combinatie met polymorfisme. Vooralsnog is het verre van duidelijk waarom je dit zou moeten doen. U moet eerst programma's met tientallen of honderden klassen tegenkomen om te begrijpen dat interfaces uw leven zoveel gemakkelijker kunnen maken dan zonder.


3. Meervoudige overerving

In Java kunnen alle klassen slechts één bovenliggende klasse hebben. In andere programmeertalen kunnen klassen vaak meerdere bovenliggende klassen hebben. Dit is erg handig, maar brengt ook veel problemen met zich mee.

De makers van Java kwamen tot een compromis: ze verbood meervoudige overerving van klassen, maar stond meervoudige overerving van interfaces toe. Een interface kan meerdere bovenliggende interfaces hebben. Een klasse kan meerdere bovenliggende interfaces hebben, maar slechts één bovenliggende klasse.

Waarom hebben ze meerdere overerving van klassen verboden, maar meerdere overerving van interfaces toegestaan? Vanwege het zogenaamde diamantoverervingsprobleem:

Meerdere erfenissen

Wanneer de B-klasse de A-klasse erft, weet het niets over de C- en D-klassen. Dus het gebruikt de variabelen van de A-klasse naar eigen goeddunken. De C-klasse doet hetzelfde: het gebruikt de variabelen van de A-klasse, maar op een andere manier. En dit alles resulteert in een conflict in de D-klasse.

Laten we eens kijken naar het volgende eenvoudige voorbeeld. Laten we zeggen dat we 3 klassen hebben:

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; }
}

De klasse Data slaat de valuevariabele op. Zijn afstammelingklasse XCoordinate gebruikt die variabele om de xwaarde op te slaan, en de YCoordinateafstammelingklasse gebruikt deze om de ywaarde op te slaan.

En het werkt. Afzonderlijk. Maar als we willen dat de klasse XYCoordinates zowel de klassen XCoordinateals overerft YCoordinate, krijgen we gebroken code. Deze klasse heeft de methoden van zijn voorouderklassen, maar ze zullen niet correct werken, omdat ze dezelfde value variable.

Maar omdat interfaces geen variabelen kunnen hebben, kunnen ze dit soort conflicten niet hebben. Dienovereenkomstig is meervoudige overerving van interfaces toegestaan.