Perché dovrei dichiarare una classe come una classe astratta?


40

Conosco la sintassi, le regole applicate alla classe astratta e voglio sapere l'uso di una classe astratta

La classe astratta non può essere istanziata direttamente ma può essere estesa da un'altra classe

Qual è il vantaggio di farlo?

In che modo differisce da un'interfaccia?

So che una classe può implementare più interfacce ma può estendere solo una classe astratta. È l'unica differenza tra un'interfaccia e una classe astratta?

Sono a conoscenza dell'uso di un'interfaccia. L'ho imparato dal modello di delega di eventi di AWT in Java.

In quali situazioni dovrei dichiarare la classe come una classe astratta? Quali sono i vantaggi di questo?


14
Questo è stato chiesto, lo sai. Una ricerca farà sorgere altre domande come questa. Dovresti iniziare su Google, che ti porterà allo Stack Overflow. Tutti questi sono duplicati: stackoverflow.com/search?q=abstract+interface .
S.Lott

1
"utilizzo della classe astratta discutono solo di ... regole". Che cosa? Cosa c'è di diverso tra "regole" e "utilizzo"? A proposito, è stata posta la tua domanda esatta. Lo so. Ho risposto. Continuare a cercare. È importante imparare come utilizzare la ricerca.
S.Lott

Voglio dire "In quali situazioni dovrei dichiarare la classe come classe astratta? Quali sono i vantaggi di ciò?"
Vaibhav Jani,

Un'interfaccia è una pura classe astratta, tutto qui. Sono una cosa sola. Uso sempre la classe astratta per una funzione nella classe base che non può essere implementata perché necessita di dati che hanno solo le sottoclassi, ma voglio assicurarmi che ogni sottoclasse abbia questa funzione e la implementa di conseguenza.
AngryBird,

Trovo che il modello di modello sia molto potente e un buon caso d'uso per le classi astratte.
m3th0dman,

Risposte:


49

Questa risposta fa un buon lavoro nel spiegare le differenze tra una classe astratta e un'interfaccia, ma non risponde al motivo per cui dovresti dichiararne una.

Da un punto di vista puramente tecnico, non è mai necessario dichiarare una classe come astratta.

Considera le seguenti tre classi:

class Database { 
    public String[] getTableNames() { return null; } //or throw an exception? who knows...
}

class SqlDatabase extends Database { } //TODO: override getTableNames

class OracleDatabase extends Database { }  //TODO: override getTableNames

Non è necessario rendere astratta la classe Database, anche se esiste un evidente problema con la sua implementazione: quando si scrive questo programma, è possibile digitare new Database()e sarebbe valido, ma non funzionerebbe mai.

Indipendentemente da ciò, otterresti comunque il polimorfismo, quindi fino a quando il tuo programma fa solo istanze SqlDatabasee OracleDatabase, puoi scrivere metodi come:

public void printTableNames(Database database) {
    String[] names = database.getTableNames();
}

Le classi astratte migliorano la situazione impedendo a uno sviluppatore di creare un'istanza della classe di base, poiché uno sviluppatore l'ha contrassegnata come priva di funzionalità . Fornisce inoltre sicurezza in fase di compilazione in modo da poter garantire che tutte le classi che estendono la tua classe astratta forniscano la minima funzionalità minima per funzionare, e non devi preoccuparti di mettere metodi stub (come quello sopra) che gli eredi hanno in qualche modo sapere magicamente che devono scavalcare un metodo per farlo funzionare.

Le interfacce sono un argomento totalmente separato. Un'interfaccia consente di descrivere quali operazioni possono essere eseguite su un oggetto. Normalmente useresti interfacce quando scrivi metodi, componenti, ecc. Che usano i servizi di altri componenti, oggetti, ma non ti importa quale sia il tipo reale di oggetto da cui stai ottenendo i servizi.

Considera il seguente metodo:

public void saveToDatabase(IProductDatabase database) {
     database.addProduct(this.getName(), this.getPrice());
}

Non ti importa se l' databaseoggetto eredita da un particolare oggetto, ti importa solo che abbia un addProductmetodo. Quindi, in questo caso, un'interfaccia è più adatta che far sì che tutte le tue classi ereditino dalla stessa classe base.

A volte la combinazione dei due funziona molto bene. Per esempio:

abstract class RemoteDatabase implements IProductDatabase { 
    public abstract String[] connect();
    public abstract void writeRow(string col1, string col2);

    public void addProduct(String name, Double price) {
        connect();
        writeRow(name, price.toString());
    }
}

class SqlDatabase extends RemoteDatabase {
    //TODO override connect and writeRow
}

class OracleDatabase extends RemoteDatabase { 
    //TODO override connect and writeRow
}

class FileDatabase implements IProductDatabase {
    public void addProduct(String name, Double price) {
         //TODO: just write to file
    }
}

Notare come alcuni database ereditano da RemoteDatabase per condividere alcune funzionalità (come la connessione prima di scrivere una riga), ma FileDatabase è una classe separata che implementa solo IProductDatabase.


16

Somiglianze

Classi e interfacce astratte sono necessarie per l'astrazione. Non possono essere istanziati con un nuovo , ma è possibile risolverli in inversione di contenitori di controllo o tramite schemi di fabbrica.

Differenza

  1. interfacce

    • Definire appalti pubblici ben noti, abilità del tipo
    • Applicabile per mostrare l'ereditarietà orizzontale, ovvero la ramificazione al primo livello di ereditarietà (ad es. ILog per definire le strutture di registrazione su database, file di testo, XML, SOAP ecc.)
    • Tutti i membri sono pubblici
    • Nessuna implementazione consentita
    • L'ereditarietà figlio può avere molte interfacce da implementare
    • Utile per l'integrazione di terze parti
    • La denominazione di solito inizia con I
  2. Classe astratta

    • Definire struttura, identità e alcuni comportamenti supportati predefiniti
    • Applicabile per mostrare l'ereditarietà verticale, ovvero la ramificazione profonda su più livelli (ad es. Classe AbstractEntity nello sviluppo guidato dal dominio)
    • I membri possono avere visibilità diversa (da pubblica a privata)
    • È possibile implementare alcuni membri (ad es. * Classi Reader)
    • Il figlio ereditario può avere solo una classe astratta di base

In realtà è facile trovare la risposta con una semplice query di Google .


che cosa mena dall'implementazione non è consentita? la classe java può implementare interfacce.
Sajuuk,

@Sajuuk quella linea si riferisce a un'interfaccia. Non è possibile inserire l'implementazione di un contratto in un'interfaccia. Potresti avere l'implementazione predefinita di un contratto in una classe astratta.
oleksii,

11

In che modo differisce da un'interfaccia?

In una classe astratta, è possibile implementare alcuni metodi e lasciare (forzare) il resto ad essere implementato dalla classe di estensione. Non è possibile implementare metodi in un'interfaccia. Non puoi costringere nessuno a scavalcare nulla quando estendi una classe normale. Con una classe astratta, puoi.


Questo ha davvero bisogno di un aggiornamento per Java 8, dove sono stati introdotti metodi predefiniti.
Haakon Løtveit,

8

Le classi astratte sono per "è un" relazioni e le interfacce sono per "può fare".

Le classi astratte ti consentono di aggiungere un comportamento di base in modo che i programmatori non debbano codificare tutto, costringendoli comunque a seguire il tuo progetto.


3

Oltre ai dettagli tecnici profondi - come l'implementazione di alcuni metodi per le classi astratte, ecc., Il significato è così:

Le interfacce definiscono capacità comuni - IEnumerable definisce che la classe che implementa questa interfaccia può essere enumerata. Non dice nulla sulla classe stessa.

Le classi astratte (o di base) definiscono il comportamento: WebRequest definisce un comportamento comune di tutte le classi figlio come HttpWebRequest, ecc. Definisce il significato principale della classe e il suo scopo reale: accedere alle risorse web.


2

Voce di Wikipedia .

Le principali differenze tra un'interfaccia e una classe astratta è che una classe astratta può fornire metodi implementati. Con le interfacce puoi solo dichiarare metodi, scrivere la loro firma. Ecco un esempio di una classe che estende una classe astratta che implementa due interfacce: (java)

interface MyInterface1 {
  string getValue1();
}

interface MyInterface2 {
  string getValue2();
}

abstract class MyAbstractClass implements MyInterface1, MyInterface2{
  void printValues() {
    System.out.println("Value 1: " + getValue1() + ", Value 2: " + getValue2() + 
                       ", Value 3: " + getValue3());
  }

  protected abstract string getValue3();
}

class ImpClass extends MyAbstractClass {
  public string getValue1() {
    return "1";
  }

  public string getValue2() {
    return "2";
  }

  protected string getValue3() {
    return "3";
  }
}

In questo esempio, MyAbstractClass fornisce un metodo pubblico che stampa tutti e tre i valori. In ImpClass è necessario implementare rispettivamente getValue1 e getValue2 da MyInterface1 e MyInterface2 e getValue3 dalla classe astratta.

Ecco.

Ci sono più aspetti (interfaccia: solo metodi pubblici, classe astratta: metodi astratti protetti e astratti pubblici) ma puoi leggerlo da solo.

In una nota finale, una classe astratta che fornisce solo metodi astratti è una classe base astratta "pura", ovvero un'interfaccia.


2
  • Interfaccia: quando alcune classi condividono un'API (nomi e parametri del metodo)
  • Classe astratta - quando alcune classi condividono lo stesso codice (implementazione)

In altre parole, dovresti iniziare con una domanda: "queste classi condividono necessariamente l' implementazione o hanno solo un'interfaccia comune ?"

Se la risposta è mista, come ad esempio - queste tre classi devono condividere l'implementazione, ma queste altre due condividono solo la loro API - allora puoi creare un'interfaccia per tutti e cinque e una classe astratta per quelle tre con il comune codice.

Esistono anche altri modi per condividere l'implementazione, ad esempio per incapsulare un oggetto con tale implementazione (ad es. Nel modello di strategia ).


1

Dichiareresti un abstract di classe quando non vuoi che lo sviluppatore (probabilmente te stesso) sia autorizzato a crearne un'istanza, perché non funzionerebbe o non avrebbe senso.

Ad esempio, considera un gioco in cui esistono diversi tipi di entità di gioco. Tutti ereditano dalla GameEntityclasse base .

abstract class GameEntity{

    int lifePoint, speed, damage;

    public attack(GameEntity target){ target.damage(damage); }

    public damage(int damageInflicted){ lifePoint -= damageInflicted - speed; }

    // etc...

}

Questa classe è dichiarata abstractpoiché non avrebbe senso istanziarla. Dichiara alcune azioni per le entità di gioco e alcuni attributi, ma da nessuna parte in questa classe questi attributi sono inizializzati. Questa classe funge da modello per le entità di gioco, ma non è pensata per essere istanziata da sola, e come tale dichiarata abstract.

Per quanto riguarda la differenza di utilizzo tra una classe astratta e un'interfaccia:

A mio avviso, un'interfaccia è un modo per ottenere un comportamento polimorfico senza essere limitato dal meccanismo di ereditarietà singola di alcune lingue.

Torniamo al gioco come esempio. Considera una classe da Enemycui deriva GameEntity. Questa classe ha un metodo attackMeFromDistance(RangedAttacker attacker). Questo metodo ha lo scopo di consentire alle entità di attaccare il nemico da molto lontano.

Come puoi vedere, questo metodo accetta un RangedAttackertipo come parametro. Tuttavia, tutte le entità di gioco ereditano già GameEntity. Non possono estendere un'altra classe.

Prendi le lezioni Magee Archerper esempio. Vogliamo consentire che entrambi vengano accettati come parametri nel attackMeFromDistance(RangedAttacker attacker)metodo, ma sono già derivati GameEntity.

Per risolvere questo, creiamo una nuova interfaccia:

interface RangedAttacker{
    public void attackFromDistance();
}

Una classe che implementa questa interfaccia deve implementare il attackFromDistance()metodo, e quindi si assicura che abbia una gamma di capacità di attacco. Ciò significa che il attackMeFromDistancemetodo ora può accettare in modo sicuro le classi che implementano questa interfaccia. Quindi realizzare Magee Archerimplementare quell'interfaccia risolve il nostro problema.

Per me, questo è il potere delle interfacce.

Quindi per riassumere, di solito useresti una classe astratta quando vuoi avere una classe base per alcune classi, ma non avrebbe senso istanziarla da sola (o nel caso in cui abbia dei abstractmetodi, che devono essere implementato dalle sottoclassi, e in questo caso il compilatore ti impone di creare la classe abstract). Utilizzeresti un'interfaccia per ottenere un comportamento polimorfico senza essere limitato dal meccanismo di ereditarietà singola.


0
  1. È possibile che in alcune classi siano comuni troppi metodi (logica aziendale). E i metodi rimanenti sono diversi. In quel tipo di scenari è possibile implementare tutti i metodi comuni in una classe e dichiarare il rimanente come astratto. Quindi dovresti dichiarare la classe come astratta.
  2. A volte non si consente di creare direttamente l'oggetto nella classe. Quel tipo di classe che devi dichiarare astratta.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.