JavaRush /Java Blog /Random-IT /Pausa caffè #140. Classi astratte e interfacce in Java

Pausa caffè #140. Classi astratte e interfacce in Java

Pubblicato nel gruppo Random-IT
Fonte: InfoWorld Oggi imparerai in quali casi uno sviluppatore dovrebbe utilizzare una classe astratta e in quali casi un'interfaccia. Identificheremo anche le differenze tra questi elementi del linguaggio Java e come utilizzarli nei programmi. Pausa caffè #140.  Classi astratte e interfacce in Java - 1Le classi e le interfacce astratte sono abbastanza comuni nel codice Java e persino nello stesso Java Development Kit (JDK). Ognuno di questi elementi ha uno scopo diverso:
  • Un'interfaccia è un costrutto nel linguaggio Java che aiuta a implementare metodi astratti e costanti statiche.
  • Le classi astratte sono simili alle classi normali, tranne per il fatto che possono includere metodi astratti, ovvero metodi senza corpo. Non è possibile creare classi astratte.
Molti sviluppatori pensano che le interfacce e le classi astratte siano simili, ma in realtà questo non è del tutto vero. Diamo un'occhiata alle principali differenze tra loro.

Cos'è un'interfaccia

Fondamentalmente, un'interfaccia è un contratto, quindi dipende dall'implementazione definire lo scopo della sua creazione. L'interfaccia non può utilizzare variabili di istanza mutabili, può utilizzare solo variabili finali.

Quando utilizzare le interfacce

Le interfacce sono molto utili per separare il codice e implementare il polimorfismo. Possiamo vederlo nel JDK con l' interfaccia List :
public interface List<E> extends Collection<E> {

    int size();
    boolean isEmpty();
    boolean add(E e);
    E remove(int index);
    void clear();
}
Come probabilmente avrai notato, questo codice, sebbene breve, è piuttosto descrittivo. Possiamo facilmente vedere la firma del metodo che verrà utilizzata per implementare i metodi nell'interfaccia utilizzando la classe concreta. L' interfaccia List contiene un contratto che può essere implementato da ArrayList , Vector , LinkedList e altre classi. Per utilizzare il polimorfismo, possiamo semplicemente dichiarare il tipo della nostra variabile utilizzando List e quindi selezionare una qualsiasi delle istanze disponibili. Ecco un altro esempio:
List list = new ArrayList();
System.out.println(list.getClass());

 List list = new LinkedList();
 System.out.println(list.getClass());
L'output è:
classe java.util.ArrayList classe java.util.LinkedList
In questo caso, i metodi di implementazione per ArrayList , LinkedList e Vector sono diversi, il che rappresenta uno scenario eccellente per l'utilizzo dell'interfaccia. Se noti che molte classi appartengono a una classe genitore con le stesse azioni del metodo ma un comportamento diverso. In tali situazioni, si consiglia di utilizzare l'interfaccia. Successivamente, esaminiamo diverse opzioni per l'utilizzo delle interfacce.

Sostituzione del metodo dell'interfaccia

Come già sappiamo, un'interfaccia è una sorta di contratto che deve essere implementato da una classe concreta. I metodi dell'interfaccia sono implicitamente astratti e richiedono un'implementazione concreta della classe. Ecco un esempio:
public class OverridingDemo {
  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }
}

interface Challenger {
  void doChallenge();
}

class JavaChallenger implements Challenger {
  @Override
  public void doChallenge() {
    System.out.println("Challenge done!");
  }
}
Conclusione:
Sfida fatta!
Si noti che i metodi dell'interfaccia sono implicitamente astratti. Ciò significa che non è necessario dichiararli esplicitamente astratti.

Variabili costanti

Un'altra regola da ricordare è che un'interfaccia può contenere solo variabili costanti. Ecco un esempio:
public class Challenger {

  int number = 7;
  String name = "Java Challenger";

}
Qui entrambe le variabili sono implicite final e static . Ciò significa che sono costanti, indipendenti dall'istanza e non possono essere modificati. Ora proveremo a modificare le variabili nell'interfaccia Challenger , diciamo in questo modo:
Challenger.number = 8;
Challenger.name = "Another Challenger";
Ciò causerà un errore di compilazione:
Impossibile assegnare un valore alla variabile finale "numero" Impossibile assegnare un valore alla variabile finale "nome"

Metodi predefiniti

Quando i metodi predefiniti furono introdotti in Java 8, alcuni sviluppatori pensarono che sarebbero stati equivalenti alle classi astratte. Tuttavia, questo non è vero perché le interfacce non possono avere uno stato. Un metodo predefinito può avere un'implementazione, ma i metodi astratti no. I metodi predefiniti sono il risultato di innovazioni con espressioni e flussi lambda, ma dobbiamo usarli con attenzione. Il metodo nel JDK che utilizza il metodo predefinito è forEach() , che fa parte dell'interfaccia Iterable . Invece di copiare il codice in ciascuna implementazione Iterable , possiamo semplicemente riutilizzare il metodo forEach :
default void forEach(Consumer<? super T> action) {
  // Code implementation here...
Qualsiasi implementazione Iterable può utilizzare il metodo forEach() senza richiedere una nuova implementazione del metodo. Possiamo quindi riutilizzare il codice con il metodo predefinito. Creiamo il nostro metodo predefinito:
public class DefaultMethodExample {

  public static void main(String[] args) {
    Challenger challenger = new JavaChallenger();
    challenger.doChallenge();
  }

}

class JavaChallenger implements Challenger { }

interface Challenger {

  default void doChallenge() {
    System.out.println("Challenger doing a challenge!");
  }
}
Risultato:
Sfidante che lancia una sfida!
Per quanto riguarda i metodi predefiniti, è importante notare che ciascuno di questi metodi deve essere implementato. Il metodo predefinito non può essere statico. Passiamo ora alle classi astratte.

L'essenza di una classe astratta

Le classi astratte possono avere uno stato con variabili di istanza. Ciò significa che la variabile di istanza può essere utilizzata e modificata. Ecco un esempio:
public abstract class AbstractClassMutation {

  private String name = "challenger";

  public static void main(String[] args) {
    AbstractClassMutation abstractClassMutation = new AbstractClassImpl();
    abstractClassMutation.name = "mutated challenger";
    System.out.println(abstractClassMutation.name);
  }

}

class AbstractClassImpl extends AbstractClassMutation { }
Conclusione:
sfidante mutato

Metodi astratti in classi astratte

Come le interfacce, le classi astratte possono avere metodi astratti. Un metodo astratto è un metodo senza corpo. A differenza delle interfacce, i metodi astratti nelle classi astratte devono essere dichiarati esplicitamente astratti. Ecco un esempio:
public abstract class AbstractMethods {

  abstract void doSomething();

}
Ed ecco un tentativo di dichiarare un metodo senza un'implementazione e senza la parola chiave astratta :
public abstract class AbstractMethods {
   void doSomethingElse();
}
Sfortunatamente, il risultato è un errore di compilazione:
Corpo del metodo mancante o dichiarazione astratta

Quando utilizzare le classi astratte

Una classe astratta è consigliata quando è necessario implementare lo stato mutabile. Ad esempio, Java Collections Framework include una classe AbstractList che utilizza lo stato delle variabili. Nei casi in cui non è necessario mantenere lo stato della classe, in genere è meglio utilizzare un'interfaccia.

Differenze tra classi astratte e interfacce

Da una prospettiva di programmazione orientata agli oggetti, la differenza principale tra un'interfaccia e una classe astratta è che un'interfaccia non può avere uno stato, mentre una classe astratta può avere uno stato con variabili di istanza. Un'altra differenza fondamentale è che le classi possono implementare più di un'interfaccia, ma possono estendere solo una classe astratta. Questa soluzione si basa sul fatto che l'ereditarietà multipla (estensione di più di una classe) può portare a un blocco del codice. Gli sviluppatori del linguaggio Java hanno deciso di evitarlo. Un'altra differenza è che le interfacce possono essere implementate da classi o estese da interfacce, ma le classi possono solo essere estese. È importante notare che le espressioni lambda possono essere utilizzate solo con un'interfaccia funzionale (ovvero un'interfaccia con un solo metodo), mentre le classi astratte con un solo metodo astratto non possono utilizzare le espressioni lambda. Ecco alcune ulteriori differenze tra classi astratte e interfacce. Interfaccia:
  • Può avere solo variabili statiche finali. Un'interfaccia non può mai cambiare il proprio stato.
  • Una classe può implementare più interfacce.
  • Può essere implementato utilizzando la parola chiave implements. Un'interfaccia può estendere un'altra interfaccia.
  • I metodi possono utilizzare solo campi finali statici, parametri o variabili locali.
  • Solo le interfacce funzionali possono utilizzare la funzione lambda in Java.
  • Non può avere un costruttore.
  • Può avere metodi astratti.
  • Può avere metodi predefiniti e statici (introdotti in Java 8).
  • Può avere metodi privati ​​con implementazione (introdotti in Java 9).
Classi astratte:
  • Può avere qualsiasi istanza o variabile statica, mutabile o immutabile.
  • Una classe può estendere solo una classe astratta.
  • Può avere un'istanza di campi mutabili, parametri o variabili locali.
  • Le classi astratte con un solo metodo astratto non possono utilizzare espressioni lambda.
  • Può avere un costruttore.
  • Può avere qualsiasi metodo.
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION