1. Prezentarea interfețelor

Astăzi este ziua ta pentru cunoaștere. Un alt subiect nou și interesant sunt interfețele.

Conceptul de interfață este copilul principiilor abstractizării și polimorfismului. O interfață este foarte asemănătoare cu o clasă abstractă, în care toate metodele sunt abstracte. Este declarată în același mod ca o clasă, dar folosim cuvântul interfacecheie.

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

Iată câteva fapte utile despre interfețe:

1. Declararea unei interfețe

interface Drawable
{
   void draw();
}

interface HasValue
{
   int getValue();
}
  1. În loc de classcuvântul cheie, scriem interface.
  2. Conține doar metode abstracte (nu scrieți abstractcuvântul cheie)
  3. De fapt, interfețele au toatepublic metodele
2. Moștenirea interfeței

O interfață poate moșteni doar interfețe. Dar o interfață poate avea mulți părinți. Un alt mod de a spune acest lucru este de a spune că Java are moștenire multiplă de interfețe. Exemple:

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

3. Moștenirea claselor de la interfețe

O clasă poate moșteni mai multe interfețe (doar de la o clasă). Acest lucru se face folosind implementscuvântul cheie. Exemplu:

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

Clasa ChessItem este declarată abstractă: implementează toate metodele moștenite, cu excepția draw. Cu alte cuvinte, ChessItemclasa conține o metodă abstractă — draw().

Semnificația tehnică a cuvintelor cheie extendsși implementseste aceeași: ambele sunt moștenire. Distincția a fost făcută pentru a îmbunătăți lizibilitatea codului. De asemenea, spunem că clasele sunt moștenite (prin extends) și interfețele sunt implementate (prin implements)

4. Variabile

Iată cel mai important lucru: variabilele obișnuite nu pot fi declarate în interfețe (deși cele statice pot).

Dar de ce avem nevoie de interfețe? Când sunt folosite? Interfețele au două avantaje puternice față de clase:



2. Separarea „descrierea metodelor” de implementarea acestora.

Anterior, am spus că dacă doriți să permiteți ca metodele clasei dvs. să fie apelate din alte clase, atunci metodele dvs. trebuie să fie marcate cu cuvântul publiccheie. Dacă doriți ca unele dintre aceste metode să fie apelate numai din cadrul clasei dvs., trebuie să le marcați cu cuvântul privatecheie. Cu alte cuvinte, împărțim metodele clasei în două categorii: „pentru folosirea tuturor” și „numai pentru uzul nostru”.

Interfețele ajută la consolidarea acestei diviziuni în continuare. Vom face o „clasă specială pe care să o folosească toată lumea”, precum și o a doua clasă „doar pentru uzul nostru”, care va moșteni prima clasă. Iată aproximativ cum ar arăta:

Inainte de După
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());
}

Ne împărțim clasa în două: o interfață și o clasă care moștenește interfața . Și care este avantajul aici?

Multe clase diferite pot implementa (moșteni) aceeași interfață. Și fiecare poate avea propriul său comportament. De exemplu, ArrayList LinkedListsunt două implementări diferite ale Listinterfeței.

Astfel, ascundem nu numai diferitele implementări, ci și clasa de implementare în sine (deoarece avem nevoie doar de interfața din cod). Acest lucru ne permite să fim foarte flexibili: chiar dacă programul rulează, putem înlocui un obiect cu altul, schimbând comportamentul unui obiect fără a afecta toate clasele care îl folosesc.

Aceasta este o tehnică foarte puternică atunci când este combinată cu polimorfismul. Deocamdată, este departe de a fi evident de ce ar trebui să faci asta. Mai întâi trebuie să întâlniți programe cu zeci sau sute de clase pentru a înțelege că interfețele vă pot face viața mult mai ușoară decât fără ele.


3. Moștenirea multiplă

În Java, toate clasele pot avea o singură clasă părinte. În alte limbaje de programare, clasele pot avea adesea mai multe clase părinte. Acest lucru este foarte convenabil, dar aduce și multe probleme.

Creatorii Java au ajuns la un compromis: au interzis moștenirea multiplă a claselor, dar au permis moștenirea multiplă a interfețelor. O interfață poate avea mai multe interfețe părinte. O clasă poate avea mai multe interfețe părinte, dar o singură clasă părinte.

De ce au interzis moștenirea multiplă a claselor, dar au permis moștenirea multiplă a interfețelor? Din cauza așa-numitei probleme de moștenire a diamantelor:

Moștenirea multiplă

Când clasa B moștenește clasa A, nu știe nimic despre clasele C și D. Deci folosește variabilele clasei A așa cum crede de cuviință. Clasa C face același lucru: folosește variabilele clasei A, dar într-un mod diferit. Și toate acestea au ca rezultat un conflict în clasa D.

Să ne uităm la următorul exemplu simplu. Să presupunem că avem 3 clase:

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

Clasa Data stochează valuevariabila. Clasa sa descendentă XCoordinate folosește acea variabilă pentru a stoca xvaloarea, iar YCoordinateclasa descendentă o folosește pentru a stoca yvaloarea.

Și funcționează. Separat. Dar dacă vrem ca clasa XYCoordinates să moștenească atât clasele XCoordinatecât și YCoordinate, atunci obținem cod rupt. Această clasă va avea metodele claselor sale strămoși, dar nu vor funcționa corect, deoarece au același value variable.

Dar pentru că interfețele nu pot avea variabile, ele nu pot avea acest tip de conflict. În consecință, este permisă moștenirea multiplă a interfețelor.