Possiamo istanziare una classe astratta?


574

Durante una mia intervista, mi è stato chiesto "Se possiamo creare un'istanza di una lezione astratta?"

La mia risposta è stata "No, non possiamo". Ma l'intervistatore mi ha detto "Sbagliato, possiamo".

Ho discusso un po 'su questo. Poi mi ha detto di provare questo da solo a casa.

abstract class my {
    public void mymethod() {
        System.out.print("Abstract");
    }
}

class poly {
    public static void main(String a[]) {
        my m = new my() {};
        m.mymethod();
    }
}

Qui, sto creando l'istanza della mia classe e chiamando il metodo della classe astratta. Qualcuno può spiegarmelo, per favore? Ho davvero sbagliato durante la mia intervista?


2
Anche se solo leggermente correlati, si può forse istanziare una classe astratta in C ++: se si deriva una classe non astratta Bda un unico astratta A, durante la parte di costruzione di Besempio, che consistono in esecuzione A's costruttore, tipo di runtime dell'oggetto è in realtà A. Solo temporaneo tuttavia.
Vlad

8
@jWeavers: l'esempio che ha dato è totalmente sbagliato. Avresti dovuto chiedere "allora a che serve la classe astratta" da lui. Se lo stai estendendo, perché stai creando un'istanza della classe estesa? È un oggetto completamente nuovo, dove finisci senza dati ..
Succo di limone,

3
O potrebbe essere l'intervistatore a voler verificare quanto sei sicuro della tua affermazione rispetto a ciò che ha proposto!
Sid,

5
Ti ha mentito. Hai lasciato cadere la palla quando non sei riuscito a sottolineare che non è ciò che fa questo codice e spiegare quali sono le sottoclassi anonime. Probabilmente lo sapeva già e voleva vedere se lo sapevi.
candied_orange

2
Non è stato un quiz, ma un colloquio di lavoro, giusto? E se Java o C ++ consentissero l'istanza di classi astratte? Non lo faresti, perché non è una cosa intelligente da fare. In Objective-C, le classi astratte sono astratte solo per convenzione e la loro istanza è un bug.
gnasher729,

Risposte:


723

Qui, sto creando l'istanza della mia classe

No, non stai creando l'istanza della tua classe astratta qui. Piuttosto stai creando un'istanza di una sottoclasse anonima della tua classe astratta. E quindi stai invocando il metodo sul riferimento di classe astratto che punta all'oggetto della sottoclasse .

Questo comportamento è chiaramente elencato in JLS - Sezione # 15.9.1 : -

Se l'espressione di creazione dell'istanza di classe termina in un corpo di classe, la classe da istanziare è una classe anonima. Poi:

  • Se T indica una classe, viene dichiarata una sottoclasse diretta anonima della classe denominata da T. È un errore in fase di compilazione se la classe indicata da T è una classe finale.
  • Se T indica un'interfaccia, viene dichiarata una sottoclasse diretta anonima di Object che implementa l'interfaccia denominata da T.
  • In entrambi i casi, il corpo della sottoclasse è il ClassBody fornito nell'espressione di creazione dell'istanza di classe.
  • La classe che viene istanziata è la sottoclasse anonima.

Enfasi mia.

Inoltre, in JLS - Sezione # 12.5 , puoi leggere il processo di creazione degli oggetti . Citerò una dichiarazione da quella qui: -

Ogni volta che viene creata una nuova istanza di classe, lo spazio di memoria viene allocato per essa con spazio per tutte le variabili di istanza dichiarate nel tipo di classe e tutte le variabili di istanza dichiarate in ciascuna superclasse del tipo di classe, comprese tutte le variabili di istanza che possono essere nascoste.

Appena prima che venga restituito un riferimento all'oggetto appena creato, il costruttore indicato viene elaborato per inizializzare il nuovo oggetto usando la seguente procedura:

Puoi leggere la procedura completa sul link che ho fornito.


Per vedere praticamente che la classe che viene istanziata è una sottoclasse anonima , devi solo compilare entrambe le classi. Supponiamo di mettere quelle classi in due file diversi:

My.java:

abstract class My {
    public void myMethod() {
        System.out.print("Abstract");
    }
}

Poly.java:

class Poly extends My {
    public static void main(String a[]) {
        My m = new My() {};
        m.myMethod();
    }
}

Ora compila entrambi i tuoi file sorgente:

javac My.java Poly.java

Ora nella directory in cui hai compilato il codice sorgente, vedrai i seguenti file di classe:

My.class
Poly$1.class  // Class file corresponding to anonymous subclass
Poly.class

Vedi quella classe - Poly$1.class. È il file di classe creato dal compilatore corrispondente alla sottoclasse anonima creata da un'istanza utilizzando il codice seguente:

new My() {};

Quindi, è chiaro che è stata istanziata una classe diversa. È solo che, a quella classe viene assegnato un nome solo dopo la compilazione da parte del compilatore.

In generale, tutte le sottoclassi anonime della tua classe saranno nominate in questo modo:

Poly$1.class, Poly$2.class, Poly$3.class, ... so on

Quei numeri indicano l'ordine in cui quelle classi anonime compaiono nella classe allegata.


173
@programmatori informatici. La risposta esatta è: - Non puoi istanziare la tua classe astratta, tuttavia puoi istanziare una sottoclasse concreta della tua classe astratta.
Rohit Jain,

17
In una riga puoi dire: - Non puoi mai istanziare una classe astratta. Questo è lo scopo di una classe astratta.
Rahul Tripathi,

66
Sembra che l'intervistatore fosse più interessato alla sua risposta che alla sua ...
Neil T.

7
Secondo un altro commento (con un riferimento JLS ), "Si dice che un oggetto sia un'istanza della sua classe e di tutte le superclassi della sua classe" - quindi non stiamo creando tecnicamente un'istanza della classe astratta qui? cioè istanziare la classe astratta?
Arshajii,

6
@ARS Direi che c'è una differenza tra l'essere un instance ofe instantiating. Istanzia solo una classe, mentre l'oggetto che crei può essere un'istanza di più classi a causa dell'ereditarietà.
Simon Forsberg,

90

Quanto sopra crea un'istanza di una classe interna anonima che è una sottoclasse della myclasse astratta. Non è strettamente equivalente all'istanza della classe astratta stessa. OTOH, ogni istanza di sottoclasse è un'istanza di tutte le sue superclassi e interfacce, quindi la maggior parte delle classi astratte sono effettivamente istanziate da un'istanza di una delle loro sottoclassi concrete.

Se l'intervistatore ha appena detto "sbagliato!" senza spiegare e dato questo esempio, come unico controesempio, penso che non sappia di cosa stia parlando, però.


10
A rigor di termini, la superclasse astratta non è istanziata. Viene chiamato il costruttore per inizializzare le variabili di istanza.
Percezione

4
Sì, lo è: subclassInstance instanceof SuperClassritornerebbe vero, quindi l'oggetto è un'istanza della superclasse, il che significa che la superclasse è stata istanziata. Ma questo è solo un pignolo semantico.
JB Nizet,

5
Potrebbe essere davvero la semantica. Java definisce l' istanza in termini di creazione di oggetti tramite la nuova parola chiave (che spesso non è possibile fare con una classe astratta). Ma ovviamente la sottoclasse concreta riporterà correttamente che si tratta di un'istanza di ogni membro della sua gerarchia padre.
Percezione

11
il paragrafo 4.12.6 del JLS dice: "Si dice che un oggetto sia un'istanza della sua classe e di tutte le superclassi della sua classe.".
JB Nizet,

85

= my() {};vuol dire che c'è un'implementazione anonima, non è semplice istanza di un oggetto, che avrebbe dovuto essere: = my(). Non puoi mai istanziare una classe astratta.


30

Solo osservazioni che potresti fare:

  1. Perché si polyestende my? Questo è inutile ...
  2. Qual è il risultato della compilation? Tre file: my.class, poly.classepoly$1.class
  3. Se possiamo istanziare una classe astratta come quella, possiamo anche istanziare un'interfaccia ... strano ...


Possiamo istanziare una classe astratta?

No, non possiamo. Quello che possiamo fare è creare una classe anonima (che è il terzo file) e crearne un'istanza.


Che ne dici di un'istanza di classe super?

La superclasse astratta non è creata da noi ma da Java.

EDIT: chiedigli di testarlo

public static final void main(final String[] args) {
    final my m1 = new my() {
    };
    final my m2 = new my() {
    };
    System.out.println(m1 == m2);

    System.out.println(m1.getClass().toString());
    System.out.println(m2.getClass().toString());

}

l'output è:

false
class my$1
class my$2

+1 per l'osservazione 3: per esempio, possiamo fare Serializable s = new Serializable() {};(che è abbastanza inutile) e se contrassegnati sul vostro codice darebbe class my$3(o qualsiasi altra cosa che racchiude classe e numero)
Ripristinare Monica - notmaynard

18

Puoi semplicemente rispondere, in una sola riga

No , non puoi mai istanziare la classe astratta

Ma l'intervistatore non è ancora d'accordo, quindi puoi dirglielo

tutto quello che puoi fare è creare una classe anonima.

E, secondo la classe Anonimo, la classe ha dichiarato e istanziato nello stesso posto / linea

Quindi, potrebbe essere possibile che l'intervistatore sia interessato a verificare il tuo livello di confidenza e quanto sai degli OOP.


17

La parte tecnica è stata ben coperta nelle altre risposte, e finisce principalmente con:
"Ha torto, non conosce le cose, chiedigli di unirsi a SO e di chiarire tutto :)"

Vorrei affrontare il fatto (che è stato menzionato in altre risposte) che questa potrebbe essere una domanda di stress ed è uno strumento importante per molti intervistatori per saperne di più su di te e su come reagire a situazioni difficili e insolite. Dandoti codici errati, probabilmente voleva vedere se hai discusso. Per sapere se hai la sicurezza di opporti ai tuoi anziani in situazioni simili a questa.

PS: Non so perché, ma ho la sensazione che l'intervistatore abbia letto questo post.


13

Le classi astratte non possono essere istanziate, ma possono essere sottoclassate. Vedi questo link

L'esempio migliore è

Sebbene la classe Calender abbia un metodo astratto getInstance () , ma quando lo diciCalendar calc=Calendar.getInstance();

calc si riferisce all'istanza di classe della classe GregorianCalendar come "GregorianCalendar estende il calendario "

Infatti il tipo interno anonimo consente di creare una sottoclasse senza nome della classe astratta e un'istanza di questo.


11

Risposta tecnica

Le classi astratte non possono essere istanziate - questo è per definizione e design.

Dal JLS, capitolo 8. Classi:

Una classe nominata può essere dichiarata astratta (§8.1.1.1) e deve essere dichiarata astratta se viene implementata in modo incompleto; tale classe non può essere istanziata, ma può essere estesa da sottoclassi.

Da JSE 6 java doc per Classes.newInstance ():

InstantiationException - se questa classe rappresenta una classe astratta, un'interfaccia, una classe di array, un tipo primitivo o un vuoto; o se la classe non ha un costruttore nullo; o se l'istanza non riesce per qualche altro motivo.

È possibile, ovviamente, creare un'istanza di una sottoclasse concreta di una classe astratta (inclusa una sottoclasse anonima) ed eseguire anche un typecast di un riferimento a un oggetto in un tipo astratto.

Un punto di vista diverso su questo: gioco di squadra e intelligenza sociale:

Questo tipo di equivoco tecnico si verifica spesso nel mondo reale quando ci occupiamo di tecnologie complesse e specifiche legali.

"Abilità della gente" può essere più importante qui di "Abilità tecniche". Se cerchi in modo competitivo e aggressivo di dimostrare la tua parte dell'argomentazione, allora potresti avere teoricamente ragione, ma potresti anche fare più danni nell'avere una "faccia" di combattimento / danno / creare un nemico di quanto valga la pena. Sii conciliante e comprensivo nel risolvere le tue differenze. Chissà - forse hai "entrambe ragione" ma stai lavorando su significati leggermente diversi per termini ??

Chissà - anche se non è probabile, è possibile che l'intervistatore abbia deliberatamente introdotto un piccolo conflitto / malinteso per metterti in una situazione difficile e vedere come ti comporti emotivamente e socialmente. Sii gentile e costruttivo con i colleghi, segui i consigli degli anziani e segui dopo l'intervista per risolvere qualsiasi sfida / equivoco - via e-mail o telefonata. Mostra che sei motivato e orientato ai dettagli.


7

È un fatto consolidato che nonabstract class può essere istanziato poiché tutti hanno risposto.

Quando il programma definisce una classe anonima, il compilatore crea effettivamente una nuova classe con un nome diverso (ha lo schema EnclosedClassName$nwheren trova il numero di classe anonimo)

Quindi se decompili questa classe Java troverai il codice come di seguito:

la mia classe

abstract class my { 
    public void mymethod() 
    { 
        System.out.print("Abstract"); 
    }
} 

poly $ 1.class (la classe generata della "classe anonima")

class poly$1 extends my 
{
} 

ploly.cass

public class poly extends my
{
    public static void main(String[] a)
    {
        my m = new poly.1(); // instance of poly.1 class NOT the abstract my class

        m.mymethod();
    }
}

4

No, non puoi istanziare una classe astratta. Istanziamo solo una classe anonima. Nella classe astratta dichiariamo metodi astratti e definiamo solo metodi concreti.


4

Informazioni sulle classi astratte

  • Impossibile creare l'oggetto di una classe astratta
  • Può creare variabili (può comportarsi come tipi di dati)
  • Se un bambino non può ignorare almeno un metodo astratto del genitore, anche il bambino diventa astratto
  • Le classi astratte sono inutili senza classi secondarie

Lo scopo di una classe astratta è comportarsi come una base. Nella gerarchia ereditaria vedrai le classi astratte verso l'alto.


3

Puoi dire:
non possiamo creare un'istanza di una classe astratta, ma possiamo usare la newparola chiave per creare un'istanza di classe anonima semplicemente aggiungendo {}come corpo dell'attrezzo alla fine della classe astratta.


3

L'estensione di una classe non significa che stai istanziando la classe. In realtà, nel tuo caso stai creando un'istanza della sottoclasse.

Sono abbastanza sicuro che le classi astratte non consentano di iniziare. Quindi, direi di no: non puoi istanziare una classe astratta. Ma puoi estenderlo / ereditarlo.

Non puoi istanziare direttamente una classe astratta. Ma ciò non significa che non puoi ottenere indirettamente un'istanza di classe (non un'istanza di classe astratta originale). Voglio dire che non puoi istanziare la classe astratta originale, ma puoi:

  1. Crea una classe vuota
  2. Ereditalo dalla classe astratta
  3. Crea un'istanza della classe derviata

Quindi puoi accedere a tutti i metodi e proprietà in una classe astratta tramite l'istanza della classe derivata.


2

È impossibile istanziare una classe astratta. Quello che puoi davvero fare, ha implementato alcuni metodi comuni in una classe astratta e ha lasciato gli altri non implementati (dichiarandoli astratti) e lascia che il discendente concreto li attui a seconda delle loro esigenze. Quindi puoi creare una factory, che restituisce un'istanza di questa classe astratta (in realtà il suo implementatore). In fabbrica poi decidi quale attuatore scegliere. Questo è noto come modello di progettazione di fabbrica:

   public abstract class AbstractGridManager {
        private LifecicleAlgorithmIntrface lifecicleAlgorithm;
        // ... more private fields

        //Method implemented in concrete Manager implementors 
        abstract public Grid initGrid();

        //Methods common to all implementors
        public Grid calculateNextLifecicle(Grid grid){
            return this.getLifecicleAlgorithm().calculateNextLifecicle(grid);
        }

        public LifecicleAlgorithmIntrface getLifecicleAlgorithm() {
            return lifecicleAlgorithm;
        }
        public void setLifecicleAlgorithm(LifecicleAlgorithmIntrface lifecicleAlgorithm) {
            this.lifecicleAlgorithm = lifecicleAlgorithm;
        }
        // ... more common logic and getters-setters pairs
    }

L'implementatore concreto deve solo implementare i metodi dichiarati come astratti, ma avrà accesso alla logica implementata in quelle classi in una classe astratta, che non sono dichiarate astratte:

public class FileInputGridManager extends AbstractGridManager {

private String filePath;

//Method implemented in concrete Manager implementors 
abstract public Grid initGrid();

public class FileInputGridManager extends AbstractGridManager {

    private String filePath;

    //Method implemented in concrete Manager implementors 
    abstract public Grid initGrid();

    public Grid initGrid(String filePath) {
        List<Cell> cells = new ArrayList<>();
        char[] chars;
        File file = new File(filePath); // for example foo.txt
        // ... more logic
        return grid;
    }
}

Quindi finalmente la fabbrica è simile a questa:

public class GridManagerFactory {
    public static AbstractGridManager getGridManager(LifecicleAlgorithmIntrface lifecicleAlgorithm, String... args){
        AbstractGridManager manager = null;

        // input from the command line
        if(args.length == 2){
            CommandLineGridManager clManager = new CommandLineGridManager();
            clManager.setWidth(Integer.parseInt(args[0]));
            clManager.setHeight(Integer.parseInt(args[1]));
            // possibly more configuration logic
            ...
            manager = clManager;
        } 
        // input from the file
        else if(args.length == 1){
            FileInputGridManager fiManager = new FileInputGridManager();
            fiManager.setFilePath(args[0]);
            // possibly more method calls from abstract class
            ...
            manager = fiManager ;
        }
        //... more possible concrete implementors
        else{
            manager = new CommandLineGridManager();
        }
        manager.setLifecicleAlgorithm(lifecicleAlgorithm);
        return manager;
    }
}

Il destinatario di AbstractGridManager chiamerebbe i metodi su di lui e otterrebbe la logica, implementata nel discendente concreto (e parzialmente nei metodi di classe astratti) senza sapere quale sia l'implementazione concreta che ha ottenuto. Questo è anche noto come inversione di controllo o iniezione di dipendenza.


2

No, non possiamo creare l'oggetto della classe astratta, ma creare la variabile di riferimento della classe astratta. La variabile di riferimento viene utilizzata per fare riferimento agli oggetti delle classi derivate (sottoclassi della classe astratta)

Ecco l'esempio che illustra questo concetto

abstract class Figure { 

    double dim1; 

    double dim2; 

    Figure(double a, double b) { 

        dim1 = a; 

        dim2 = b; 

    } 

    // area is now an abstract method 

    abstract double area(); 

    }


    class Rectangle extends Figure { 
        Rectangle(double a, double b) { 
        super(a, b); 
    } 
    // override area for rectangle 
    double area() { 
        System.out.println("Inside Area for Rectangle."); 
        return dim1 * dim2; 
    } 
}

class Triangle extends Figure { 
    Triangle(double a, double b) { 
        super(a, b); 
    } 
    // override area for right triangle 
    double area() { 
        System.out.println("Inside Area for Triangle."); 
        return dim1 * dim2 / 2; 
    } 
}

class AbstractAreas { 
    public static void main(String args[]) { 
        // Figure f = new Figure(10, 10); // illegal now 
        Rectangle r = new Rectangle(9, 5); 
        Triangle t = new Triangle(10, 8); 
        Figure figref; // this is OK, no object is created 
        figref = r; 
        System.out.println("Area is " + figref.area()); 
        figref = t; 
        System.out.println("Area is " + figref.area()); 
    } 
}

Qui vediamo che non possiamo creare l'oggetto di tipo Figura ma possiamo creare una variabile di riferimento di tipo Figura. Qui abbiamo creato una variabile di riferimento di tipo Figura e Figura La variabile di riferimento di classe viene utilizzata per fare riferimento agli oggetti di Classe Rettangolo e Triangolo.


0

In realtà non possiamo creare direttamente un oggetto di una classe astratta. Ciò che creiamo è una variabile di riferimento di una chiamata astratta. La variabile di riferimento viene utilizzata per fare riferimento all'oggetto della classe che eredita la classe astratta, ovvero la sottoclasse della classe 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.