1. Introduktion av gränssnitt

Idag är din dag för kunskap. Ett annat nytt och intressant ämne är gränssnitt.

Konceptet med ett gränssnitt är barnet till principerna om abstraktion och polymorfism. Ett gränssnitt är mycket likt en abstrakt klass, där alla metoder är abstrakta. Det deklareras på samma sätt som en klass, men vi använder nyckelordet interface.

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

Här är några användbara fakta om gränssnitt:

1. Deklarera ett gränssnitt

interface Drawable
{
   void draw();
}

interface HasValue
{
   int getValue();
}
  1. Istället för classnyckelordet skriver vi interface.
  2. Den innehåller bara abstrakta metoder (skriv inte nyckelordet abstract)
  3. Faktum är att gränssnitt har allapublic metoder
2. Gränssnittsarv

Ett gränssnitt kan bara ärva gränssnitt. Men ett gränssnitt kan ha många föräldrar. Ett annat sätt att säga detta är att säga att Java har flera arv av gränssnitt. Exempel:

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

3. Ärva klasser från gränssnitt

En klass kan ärva flera gränssnitt (endast från en klass). Detta görs med hjälp av implementsnyckelordet. Exempel:

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

Klassen ChessItem förklaras abstrakt: den implementerar alla ärvda metoder utom draw. Klassen innehåller med andra ord ChessItemen abstrakt metod — draw().

Den tekniska innebörden av nyckelorden extendsoch implementsär densamma: båda är arv. Distinktionen gjordes för att förbättra läsbarheten av koden. Vi säger också att klasser ärvs (via extends) och gränssnitt implementeras (via implements)

4. Variabler

Här är det viktigaste: vanliga variabler kan inte deklareras i gränssnitt (även om statiska kan).

Men varför behöver vi gränssnitt? När används de? Gränssnitt har två starka fördelar jämfört med klasser:



2. Separera "beskrivning av metoder" från deras genomförande.

Tidigare sa vi att om du vill tillåta att metoderna i din klass ska anropas från andra klasser, måste dina metoder markeras med nyckelordet public. Om du vill att några av dessa metoder endast ska anropas från din klass, måste du markera dem med nyckelordet private. Med andra ord delar vi in ​​klassens metoder i två kategorier: "för alla att använda" och "bara för vårt eget bruk".

Gränssnitt bidrar till att stärka denna division ytterligare. Vi kommer att göra en speciell "klass för alla att använda" samt en andra klass "bara för eget bruk", som kommer att ärva den första klassen. Så här skulle det se ut ungefär:

Innan 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 delar upp vår klass i två: ett gränssnitt och en klass som ärver gränssnittet . Och vad är fördelen här?

Många olika klasser kan implementera (ärva) samma gränssnitt. Och var och en kan ha sitt eget beteende. Det finns till exempel ArrayList LinkedListtvå olika implementeringar av Listgränssnittet.

Således döljer vi inte bara de olika implementeringarna, utan även själva implementeringsklassen (eftersom vi bara behöver gränssnittet i koden). Detta låter oss vara mycket flexibla: precis när programmet körs kan vi ersätta ett objekt med ett annat, ändra ett objekts beteende utan att påverka alla klasser som använder det.

Detta är en mycket kraftfull teknik i kombination med polymorfism. Just nu är det långt ifrån självklart varför du ska göra detta. Du måste först stöta på program med dussintals eller hundratals klasser för att förstå att gränssnitt kan göra ditt liv så mycket enklare än utan dem.


3. Multipelt arv

I Java kan alla klasser bara ha en överordnad klass. I andra programmeringsspråk kan klasser ofta ha flera överordnade klasser. Detta är mycket bekvämt, men ger också många problem.

Javas skapare kom fram till en kompromiss: de förbjöd multipelt arv av klasser, men tillät multipelt arv av gränssnitt. Ett gränssnitt kan ha flera överordnade gränssnitt. En klass kan ha flera överordnade gränssnitt men bara en överordnad klass.

Varför förbjöd de multipelt arv av klasser men tillät multipelt arv av gränssnitt? På grund av så kallat diamantarvsproblem:

Multipelt arv

När B-klassen ärver A-klassen vet den ingenting om C- och D-klasserna. Så den använder variablerna i klassen A som den tycker är lämplig. C-klassen gör samma sak: den använder variablerna i klassen A, men på ett annat sätt. Och allt detta resulterar i en konflikt i D-klassen.

Låt oss titta på följande enkla exempel. Låt oss säga att 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; }
}

Dataklassen lagrar valuevariabeln. Dess underklass XCoordinate använder den variabeln för att lagra xvärdet, och YCoordinateklassen understigande använder den för att lagra yvärdet.

Och det fungerar. Separat. Men om vi vill att klassen XYCoordinates ska ärva både klasserna XCoordinateoch YCoordinateklasserna får vi trasig kod. Den här klassen kommer att ha metoderna för sina förfaderklasser, men de kommer inte att fungera korrekt, eftersom de har samma value variable.

Men eftersom gränssnitt inte kan ha variabler kan de inte ha den här typen av konflikter. Följaktligen är flera arv av gränssnitt tillåtet.