Können wir eine abstrakte Klasse instanziieren?

573

Während eines meiner Interviews wurde ich gefragt: "Können wir eine abstrakte Klasse instanziieren?"

Meine Antwort war "Nein, wir können nicht". Aber der Interviewer sagte mir: "Falsch, wir können."

Ich habe ein bisschen darüber gestritten. Dann sagte er mir, ich solle es selbst zu Hause versuchen.

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

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

Hier erstelle ich eine Instanz meiner Klasse und rufe die Methode der abstrakten Klasse auf. Kann mir bitte jemand das erklären? Habe ich mich während meines Interviews wirklich geirrt?

Ravi
quelle
2
Obwohl nur wenig im Zusammenhang, kann man vielleicht eine abstrakte Klasse in C instanziiert ++: Wenn Sie eine nicht-abstrakte Klasse abgeleitet Bvon einer abstrakten ein A, während der Teil der Konstruktion von BBeispiel , die aus laufenden A‚s Konstruktor, der Laufzeittyp des Objekts ist tatsächlich A. Nur vorübergehend.
Vlad
8
@jWeavers: Das Beispiel, das er gegeben hat, ist völlig falsch. Sie hätten ihn fragen sollen, "was die Verwendung der abstrakten Klasse ist". Wenn Sie es erweitern, warum erstellen Sie dann eine Instanz der erweiterten Klasse? Es ist ein völlig neues Objekt, bei dem Sie am Ende keine Daten haben.
Zitronensaft
3
Oder vielleicht wollte der Interviewer überprüfen, wie sicher Sie in Bezug auf Ihre Aussage gegenüber dem sind, was er vorgeschlagen hat!
Sid
5
Er hat dich angelogen. Sie haben den Ball fallen lassen, als Sie nicht darauf hingewiesen haben, dass dieser Code nicht funktioniert, und erklärt, was anonyme Unterklassen sind. Er wusste das wahrscheinlich schon und wollte sehen, ob Sie es wussten.
candied_orange
2
Dies war keine Quizshow, sondern ein Vorstellungsgespräch, oder? Was wäre, wenn Java oder C ++ das Instanziieren abstrakter Klassen erlauben würden? Sie würden es nicht tun, weil es keine kluge Sache ist. In Objective-C sind abstrakte Klassen nur durch Konvention abstrakt, und das Instanziieren ist ein Fehler.
Gnasher729

Antworten:

722

Hier erstelle ich eine Instanz meiner Klasse

Nein, Sie erstellen hier nicht die Instanz Ihrer abstrakten Klasse. Sie erstellen vielmehr eine Instanz einer anonymen Unterklasse Ihrer abstrakten Klasse. Anschließend rufen Sie die Methode für Ihre abstrakte Klassenreferenz auf, die auf ein Unterklassenobjekt verweist .

Dieses Verhalten ist in JLS - Abschnitt 15.9.1 klar aufgeführt : -

Wenn der Ausdruck zur Erstellung der Klasseninstanz in einem Klassenkörper endet, ist die zu instanziierende Klasse eine anonyme Klasse. Dann:

  • Wenn T eine Klasse bezeichnet, wird eine anonyme direkte Unterklasse der von T benannten Klasse deklariert. Es ist ein Fehler zur Kompilierungszeit, wenn die mit T bezeichnete Klasse eine endgültige Klasse ist.
  • Wenn T eine Schnittstelle bezeichnet, wird eine anonyme direkte Unterklasse von Object deklariert, die die von T benannte Schnittstelle implementiert.
  • In beiden Fällen ist der Hauptteil der Unterklasse der ClassBody, der im Ausdruck zur Erstellung der Klasseninstanz angegeben ist.
  • Die zu instanziierende Klasse ist die anonyme Unterklasse.

Hervorhebung von mir.

In JLS - Abschnitt 12.5 können Sie außerdem Informationen zum Objekterstellungsprozess lesen . Ich werde hier eine Aussage daraus zitieren:

Jedes Mal, wenn eine neue Klasseninstanz erstellt wird, wird ihr Speicherplatz mit Platz für alle im Klassentyp deklarierten Instanzvariablen und alle in jeder Oberklasse des Klassentyps deklarierten Instanzvariablen zugewiesen, einschließlich aller möglicherweise ausgeblendeten Instanzvariablen.

Kurz bevor ein Verweis auf das neu erstellte Objekt als Ergebnis zurückgegeben wird, wird der angegebene Konstruktor verarbeitet, um das neue Objekt wie folgt zu initialisieren:

Den vollständigen Vorgang finden Sie unter dem von mir angegebenen Link.


Um praktisch zu sehen, dass die zu instanziierende Klasse eine anonyme Unterklasse ist , müssen Sie nur beide Klassen kompilieren. Angenommen, Sie legen diese Klassen in zwei verschiedenen Dateien ab:

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

Kompilieren Sie nun beide Quelldateien:

javac My.java Poly.java

In dem Verzeichnis, in dem Sie den Quellcode kompiliert haben, werden die folgenden Klassendateien angezeigt:

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

Siehe diese Klasse - Poly$1.class. Dies ist die vom Compiler erstellte Klassendatei, die der anonymen Unterklasse entspricht, die Sie mit dem folgenden Code instanziiert haben:

new My() {};

Es ist also klar, dass eine andere Klasse instanziiert wird. Es ist nur so, dass diese Klasse erst nach der Kompilierung durch den Compiler einen Namen erhält.

Im Allgemeinen werden alle anonymen Unterklassen in Ihrer Klasse folgendermaßen benannt:

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

Diese Zahlen bezeichnen die Reihenfolge, in der diese anonymen Klassen in der umschließenden Klasse erscheinen.

Rohit Jain
quelle
172
@coders. Die genaue Antwort lautet: - Sie können Ihre abstrakte Klasse nicht instanziieren, Sie können jedoch eine konkrete Unterklasse Ihrer abstrakten Klasse instanziieren.
Rohit Jain
16
In einer Zeile können Sie sagen: - Sie können niemals eine abstrakte Klasse instanziieren. Das ist der Zweck einer abstrakten Klasse.
Rahul Tripathi
66
Es hört sich so an, als ob der Interviewer mehr in seine Antwort investiert war als in Ihre ...
Neil T.
7
In einem anderen Kommentar (mit einer JLS-Referenz ) heißt es: "Ein Objekt soll eine Instanz seiner Klasse und aller Oberklassen seiner Klasse sein." Erstellen wir hier also nicht tatsächlich eine Instanz der abstrakten Klasse? dh die abstrakte Klasse instanziieren?
Arshajii
6
@ARS Ich würde sagen, dass es einen Unterschied zwischen einem instance ofund gibt instantiating. Sie instanziieren nur eine Klasse, während das von Ihnen erstellte Objekt aufgrund der Vererbung eine Instanz mehrerer Klassen sein kann.
Simon Forsberg
89

Das Obige instanziiert eine anonyme innere Klasse, die eine Unterklasse der myabstrakten Klasse ist. Es ist nicht unbedingt gleichbedeutend mit der Instanziierung der abstrakten Klasse selbst. OTOH, jede Unterklasseninstanz ist eine Instanz aller ihrer Superklassen und Schnittstellen, sodass die meisten abstrakten Klassen tatsächlich durch Instanziieren einer ihrer konkreten Unterklassen instanziiert werden.

Wenn der Interviewer nur "falsch" sagte! Ohne es zu erklären und dieses Beispiel als einzigartiges Gegenbeispiel zu nennen, denke ich, dass er nicht weiß, wovon er spricht.

JB Nizet
quelle
10
Genau genommen wird die abstrakte Oberklasse nicht instanziiert. Der Konstruktor wird aufgerufen, um Instanzvariablen zu initialisieren.
Wahrnehmung
4
Ja, es ist: subclassInstance instanceof SuperClasswürde true zurückgeben, daher ist das Objekt eine Instanz der Oberklasse, was bedeutet, dass die Oberklasse instanziiert wurde. Aber das ist nur semantisches Nitpicking.
JB Nizet
5
Könnte tatsächlich Semantik sein. Java definiert die Instanziierung als das Erstellen von Objekten über das neue Schlüsselwort (was Sie mit einer abstrakten Klasse nicht tun können). Aber natürlich wird die konkrete Unterklasse korrekt melden, dass es sich um eine Instanz jedes Mitglieds seiner übergeordneten Hierarchie handelt.
Wahrnehmung
11
In Absatz 4.12.6 des JLS heißt es: "Ein Objekt soll eine Instanz seiner Klasse und aller Oberklassen seiner Klasse sein."
JB Nizet
85

= my() {};bedeutet, dass es eine anonyme Implementierung gibt, nicht eine einfache Instanziierung eines Objekts, die hätte sein sollen : = my(). Sie können niemals eine abstrakte Klasse instanziieren.

Ioan
quelle
30

Nur Beobachtungen, die Sie machen könnten:

  1. Warum polyverlängert my? Das ist nutzlos ...
  2. Was ist das Ergebnis der Zusammenstellung? Drei Dateien: my.class, poly.classundpoly$1.class
  3. Wenn wir eine solche abstrakte Klasse instanziieren können, können wir auch eine Schnittstelle instanziieren ... seltsam ...


Können wir eine abstrakte Klasse instanziieren?

Nein, wir können nicht. Wir können eine anonyme Klasse erstellen (das ist die dritte Datei) und sie instanziieren.


Was ist mit einer erstklassigen Instanziierung?

Die abstrakte Superklasse wird nicht von uns, sondern von Java instanziiert .

EDIT: Bitten Sie ihn, dies zu testen

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());

}

Ausgabe ist:

false
class my$1
class my$2
ncenerar
quelle
+1 für Beobachtung 3: zB können wir tun Serializable s = new Serializable() {};(was ziemlich nutzlos ist) und wenn auf Ihrem Code markiert würde class my$3(oder was auch immer Klasse und Nummer einschließt)
Reinstate Monica - notmaynard
18

Sie können einfach in nur einer Zeile antworten

Nein , Sie können Abstract Class niemals instanziieren

Aber, Interviewer immer noch nicht einverstanden, dann können Sie ihm / ihr sagen

Sie können lediglich eine anonyme Klasse erstellen.

Und gemäß der anonymen Klasse wird die Klasse an derselben Stelle / Zeile deklariert und instanziiert

Es ist also möglich, dass der Interviewer daran interessiert ist, Ihr Konfidenzniveau und Ihr Wissen über die OOPs zu überprüfen.


quelle
17

Der technische Teil wurde in den anderen Antworten ausführlich behandelt und endet hauptsächlich mit:
"Er liegt falsch, er weiß nichts, bitte ihn, sich SO anzuschließen und alles zu klären :)"

Ich möchte auf die Tatsache eingehen (die in anderen Antworten erwähnt wurde), dass dies eine Stressfrage sein könnte und für viele Interviewer ein wichtiges Instrument ist, um mehr über Sie zu erfahren und wie Sie auf schwierige und ungewöhnliche Situationen reagieren. Indem er Ihnen falsche Codes gab, wollte er wahrscheinlich sehen, ob Sie zurückgestritten haben. Zu wissen, ob Sie das Vertrauen haben, sich in ähnlichen Situationen gegen Ihre Senioren zu behaupten.

PS: Ich weiß nicht warum, aber ich habe das Gefühl, dass der Interviewer diesen Beitrag gelesen hat.

Mixcels
quelle
13

Abstrakte Klassen können nicht instanziiert, aber in Unterklassen unterteilt werden. Siehe diesen Link

Das beste Beispiel ist

Zwar hat die Kalenderklasse eine abstrakte Methode getInstance () , aber wenn Sie sagenCalendar calc=Calendar.getInstance();

calc bezieht sich auf die Klasseninstanz der Klasse GregorianCalendar als "GregorianCalendar erweitert Kalender "

Mit dem anonymen inneren Typ von Infact können Sie eine No-Name-Unterklasse der abstrakten Klasse und eine Instanz davon erstellen .

Abhishek_Mishra
quelle
11

Technische Antwort

Abstrakte Klassen können nicht instanziiert werden - dies ist per Definition und Design.

Aus dem JLS, Kapitel 8. Klassen:

Eine benannte Klasse kann als abstrakt deklariert werden (§8.1.1.1) und muss als abstrakt deklariert werden, wenn sie unvollständig implementiert ist. Eine solche Klasse kann nicht instanziiert, sondern um Unterklassen erweitert werden.

Aus JSE 6 Java Doc für Classes.newInstance ():

InstantiationException - Wenn diese Klasse eine abstrakte Klasse, eine Schnittstelle, eine Array-Klasse, einen primitiven Typ oder eine Leere darstellt. oder wenn die Klasse keinen Nullkonstruktor hat; oder wenn die Instanziierung aus einem anderen Grund fehlschlägt.

Sie können natürlich eine konkrete Unterklasse einer abstrakten Klasse (einschließlich einer anonymen Unterklasse) instanziieren und auch eine Typumwandlung eines Objektverweises auf einen abstrakten Typ durchführen.

Ein anderer Blickwinkel - Teamplay & Social Intelligence:

Diese Art von technischem Missverständnis tritt in der realen Welt häufig auf, wenn wir uns mit komplexen Technologien und legalistischen Spezifikationen befassen.

"People Skills" können hier wichtiger sein als "Technical Skills". Wenn Sie wettbewerbsfähig und aggressiv versuchen, Ihre Seite des Arguments zu beweisen, könnten Sie theoretisch Recht haben, aber Sie könnten auch mehr Schaden anrichten, wenn Sie einen Kampf führen / ein "Gesicht" beschädigen / einen Feind erschaffen, als es wert ist. Seien Sie versöhnlich und verständnisvoll bei der Lösung Ihrer Differenzen. Wer weiß - vielleicht haben Sie "beide Recht", arbeiten aber etwas andere Bedeutungen für Begriffe aus?

Wer weiß - obwohl nicht wahrscheinlich, ist es möglich, dass der Interviewer absichtlich einen kleinen Konflikt / ein Missverständnis eingeführt hat, um Sie in eine herausfordernde Situation zu bringen und zu sehen, wie Sie sich emotional und sozial verhalten. Seien Sie freundlich und konstruktiv gegenüber Kollegen, befolgen Sie die Ratschläge von Senioren und setzen Sie sich nach dem Interview durch, um alle Herausforderungen / Missverständnisse zu lösen - per E-Mail oder Telefonanruf. Zeigt, dass Sie motiviert und detailorientiert sind.

Glen Best
quelle
7

Es ist eine allgemein anerkannte Tatsache, abstract classdie nicht instanziiert werden kann, wenn alle geantwortet haben.

Wenn das Programm eine anonyme Klasse definiert, erstellt der Compiler tatsächlich eine neue Klasse mit einem anderen Namen (hat das Muster, EnclosedClassName$nin dem nsich die anonyme Klassennummer befindet).

Wenn Sie diese Java-Klasse dekompilieren, finden Sie den folgenden Code:

meine Klasse

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

poly $ 1.class (die generierte Klasse der "anonymen Klasse")

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();
    }
}
iTech
quelle
4

Nein, Sie können eine abstrakte Klasse nicht instanziieren. Wir instanziieren nur eine anonyme Klasse. In einer abstrakten Klasse deklarieren wir abstrakte Methoden und definieren nur konkrete Methoden.

Vikas Agrahari
quelle
4

Über abstrakte Klassen

  • Objekt einer abstrakten Klasse kann nicht erstellt werden
  • Kann Variablen erstellen (kann sich wie Datentypen verhalten)
  • Wenn ein Kind mindestens eine abstrakte Methode des Elternteils nicht überschreiben kann, wird das Kind ebenfalls abstrakt
  • Abstrakte Klassen sind ohne Kinderklassen nutzlos

Der Zweck einer abstrakten Klasse besteht darin, sich wie eine Basis zu verhalten. In der Vererbungshierarchie sehen Sie abstrakte Klassen nach oben.

Priyankara
quelle
3

Sie können sagen:
Wir können eine abstrakte Klasse nicht instanziieren, aber wir können ein newSchlüsselwort verwenden, um eine anonyme Klasseninstanz zu erstellen, indem wir {}am Ende der abstrakten Klasse nur als Implementierungskörper hinzufügen .

Eddy
quelle
3

Das Erweitern einer Klasse bedeutet nicht, dass Sie die Klasse instanziieren. In Ihrem Fall erstellen Sie tatsächlich eine Instanz der Unterklasse.

Ich bin mir ziemlich sicher, dass abstrakte Klassen keine Initiierung erlauben. Also würde ich nein sagen: Sie können eine abstrakte Klasse nicht instanziieren. Aber Sie können es erweitern / erben.

Sie können eine abstrakte Klasse nicht direkt instanziieren. Dies bedeutet jedoch nicht, dass Sie eine Instanz der Klasse (nicht wirklich eine Instanz der ursprünglichen abstrakten Klasse) indirekt erhalten können. Ich meine, Sie können die ursprüngliche abstrakte Klasse nicht instanziieren, aber Sie können:

  1. Erstellen Sie eine leere Klasse
  2. Erben Sie es von der abstrakten Klasse
  3. Instanziieren Sie die abgeleitete Klasse

So erhalten Sie über die abgeleitete Klasseninstanz Zugriff auf alle Methoden und Eigenschaften einer abstrakten Klasse.

Kas
quelle
2

Es ist unmöglich, eine abstrakte Klasse zu instanziieren. Was Sie wirklich tun können, ist, einige gängige Methoden in einer abstrakten Klasse zu implementieren und andere nicht implementieren zu lassen (sie als abstrakt zu deklarieren) und sie vom konkreten Absteiger je nach ihren Anforderungen implementieren zu lassen. Dann können Sie eine Factory erstellen, die eine Instanz dieser abstrakten Klasse (tatsächlich seinen Implementierer) zurückgibt. In der Fabrik entscheiden Sie dann, welchen Implementierer Sie wählen möchten. Dies ist als Werksentwurfsmuster bekannt:

   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
    }

Der konkrete Implementierer muss nur die als abstrakt deklarierten Methoden implementieren, hat jedoch Zugriff auf die Logik, die in den Klassen einer abstrakten Klasse implementiert ist, die nicht als abstrakt deklariert sind:

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

Dann sieht die Fabrik endlich so aus:

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

Der Empfänger von AbstractGridManager würde die Methoden für ihn aufrufen und die Logik abrufen, die im konkreten Abstieg (und teilweise in den abstrakten Klassenmethoden) implementiert ist, ohne zu wissen, welche konkrete Implementierung er erhalten hat. Dies wird auch als Inversion der Kontrolle oder Abhängigkeitsinjektion bezeichnet.

Picrochole
quelle
2

Nein, wir können das Objekt der abstrakten Klasse nicht erstellen, sondern die Referenzvariable der abstrakten Klasse. Die Referenzvariable wird verwendet, um auf die Objekte abgeleiteter Klassen (Unterklassen der abstrakten Klasse) zu verweisen.

Hier ist das Beispiel, das dieses Konzept veranschaulicht

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

Hier sehen wir, dass wir das Objekt vom Typ Figure nicht erstellen können, aber wir können eine Referenzvariable vom Typ Figure erstellen. Hier haben wir eine Referenzvariable vom Typ Figure erstellt. Die Referenzvariable der Klasse Class wird verwendet, um auf die Objekte der Klassen Rectangle und Triangle zu verweisen.

Ketan G.
quelle
0

Eigentlich können wir ein Objekt einer abstrakten Klasse nicht direkt erstellen. Was wir erstellen, ist eine Referenzvariable eines abstrakten Aufrufs. Die Referenzvariable wird verwendet, um auf das Objekt der Klasse zu verweisen, die die abstrakte Klasse erbt, dh die Unterklasse der abstrakten Klasse.

Jency
quelle