Peut-on instancier une classe abstraite?

574

Lors d'une de mes interviews, on m'a demandé "Si on peut instancier une classe abstraite?"

Ma réponse a été "non, nous ne pouvons pas". Mais, l'intervieweur m'a dit "Faux, nous pouvons."

J'ai argumenté un peu là-dessus. Puis il m'a dit d'essayer moi-même à la maison.

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

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

Ici, je crée une instance de ma classe et j'appelle une méthode de classe abstraite. Quelqu'un peut-il me l'expliquer? Ai-je vraiment tort lors de mon entretien?

Ravi
la source
2
Bien que peu lié, on peut peut-être instancier une classe abstraite en C ++: si vous dérivez une classe non abstraite Bd'une classe abstraite A, pendant la partie de construction d' Binstance, qui consiste à exécuter Ale constructeur de, le type d'exécution de l'objet est en fait A. Seulement temporaire cependant.
Vlad
8
@jWeavers: L'exemple qu'il a donné est totalement faux. Vous auriez dû lui demander "alors à quoi sert la classe abstraite". Si vous l'étendez, pourquoi créez-vous une instance de la classe étendue? C'est un objet complètement nouveau, où vous vous retrouvez sans aucune donnée ..
Lemon Juice
3
Ou peut-être que l'intervieweur voulait vérifier à quel point vous êtes confiant dans votre déclaration par rapport à ce qu'il a proposé!
Sid
5
Il vous a menti. Vous avez laissé tomber la balle lorsque vous avez omis de souligner que ce n'est pas ce que fait ce code et d'expliquer ce que sont les sous-classes anonymes. Il le savait probablement déjà et voulait voir si vous saviez.
candied_orange
2
Ce n'était pas un quiz, mais un entretien d'embauche, non? Et si Java ou C ++ permettait d'instancier des classes abstraites? Vous ne le feriez pas, car ce n'est pas une chose intelligente à faire. Dans Objective-C, les classes abstraites ne sont abstraites que par convention, et les instancier est un bug.
gnasher729

Réponses:

723

Ici, je crée une instance de ma classe

Non, vous ne créez pas ici l'instance de votre classe abstraite. Vous créez plutôt une instance d'une sous - classe anonyme de votre classe abstraite. Et puis vous invoquez la méthode sur votre référence de classe abstraite pointant vers l' objet de sous-classe .

Ce comportement est clairement répertorié dans JLS - Section # 15.9.1 : -

Si l'expression de création d'instance de classe se termine par un corps de classe, la classe en cours d'instanciation est une classe anonyme. Alors:

  • Si T désigne une classe, alors une sous-classe directe anonyme de la classe nommée par T est déclarée. Il s'agit d'une erreur au moment de la compilation si la classe indiquée par T est une classe finale.
  • Si T désigne une interface, alors une sous-classe directe anonyme d'Object qui implémente l'interface nommée par T est déclarée.
  • Dans les deux cas, le corps de la sous-classe est le ClassBody donné dans l'expression de création d'instance de classe.
  • La classe en cours d'instanciation est la sous-classe anonyme.

Je souligne.

De plus, dans JLS - Section # 12.5 , vous pouvez lire sur le processus de création d'objet . Je vais en citer une déclaration ici: -

Chaque fois qu'une nouvelle instance de classe est créée, un espace mémoire lui est alloué avec de la place pour toutes les variables d'instance déclarées dans le type de classe et toutes les variables d'instance déclarées dans chaque superclasse du type de classe, y compris toutes les variables d'instance qui peuvent être masquées.

Juste avant qu'une référence à l'objet nouvellement créé soit renvoyée comme résultat, le constructeur indiqué est traité pour initialiser le nouvel objet à l'aide de la procédure suivante:

Vous pouvez lire la procédure complète sur le lien que j'ai fourni.


Pour voir pratiquement que la classe en cours d'instanciation est une sous- classe anonyme , il vous suffit de compiler vos deux classes. Supposons que vous placiez ces classes dans deux fichiers différents:

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

Maintenant, compilez vos deux fichiers source:

javac My.java Poly.java

Maintenant, dans le répertoire où vous avez compilé le code source, vous verrez les fichiers de classe suivants:

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

Voir cette classe - Poly$1.class. Il s'agit du fichier de classe créé par le compilateur correspondant à la sous-classe anonyme que vous avez instanciée à l'aide du code ci-dessous:

new My() {};

Il est donc clair qu'il existe une classe différente en cours d'instanciation. C'est juste que, cette classe ne reçoit un nom qu'après compilation par le compilateur.

En général, toutes les sous-classes anonymes de votre classe seront nommées de cette manière:

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

Ces nombres indiquent l'ordre dans lequel ces classes anonymes apparaissent dans la classe englobante.

Rohit Jain
la source
173
@coders. La réponse exacte est: - Vous ne pouvez pas instancier votre classe abstraite, mais vous pouvez instancier une sous-classe concrète de votre classe abstraite.
Rohit Jain
17
En une ligne, vous pouvez dire: - Vous ne pouvez jamais instancier une classe abstraite. C'est le but d'une classe abstraite.
Rahul Tripathi
66
On dirait que l'intervieweur était plus investi dans sa réponse qu'il ne l'était dans la vôtre ...
Neil T.
7
Selon un autre commentaire (avec une référence JLS ), "Un objet est dit être une instance de sa classe et de toutes les superclasses de sa classe" - donc, ne sommes-nous pas en train de créer techniquement une instance de la classe abstraite ici? c'est-à-dire instancier la classe abstraite?
arshajii
6
@ARS Je dirais qu'il y a une différence entre être un instance ofet instantiating. Vous instanciez uniquement une classe, tandis que cet objet que vous créez peut être une instance de plusieurs classes en raison de l'héritage.
Simon Forsberg
90

Ce qui précède instancie une classe interne anonyme qui est une sous-classe de la myclasse abstraite. Ce n'est pas strictement équivalent à l'instanciation de la classe abstraite elle-même. OTOH, chaque instance de sous-classe est une instance de toutes ses super classes et interfaces, donc la plupart des classes abstraites sont en effet instanciées en instanciant une de leurs sous-classes concrètes.

Si l'intervieweur vient de dire "mal!" sans expliquer, et a donné cet exemple, comme contre-exemple unique, je pense qu'il ne sait pas de quoi il parle, cependant.

JB Nizet
la source
10
À proprement parler, la superclasse abstraite n'est pas instanciée. Son constructeur est appelé pour initialiser les variables d'instance.
Perception
4
Oui, c'est: subclassInstance instanceof SuperClassretournerait vrai, donc l'objet est une instance de la superclasse, ce qui signifie que la superclasse a été instanciée. Mais c'est juste une piqûre sémantique.
JB Nizet
5
Ça pourrait être de la sémantique. Java définit l' instanciation en termes de création d'objets via le nouveau mot-clé (ce que vous ne pouvez pas faire avec une classe abstraite). Mais bien sûr, la sous-classe concrète indiquera correctement qu'il s'agit d'une instance de chaque membre de sa hiérarchie parent.
Perception
11
le paragraphe 4.12.6 du JLS dit: "Un objet est dit être une instance de sa classe et de toutes les superclasses de sa classe.".
JB Nizet
85

= my() {};signifie qu'il ya une mise en œuvre anonyme, pas simple instanciation d'un objet, qui aurait dû être: = my(). Vous ne pouvez jamais instancier une classe abstraite.

Ioan
la source
30

Juste des observations que vous pourriez faire:

  1. Pourquoi se polyprolonge my? C'est inutile ...
  2. Quel est le résultat de la compilation? Trois fichiers: my.class, poly.classetpoly$1.class
  3. Si nous pouvons instancier une classe abstraite comme ça, nous pouvons aussi instancier une interface ... bizarre ...


Peut-on instancier une classe abstraite?

Non, nous ne pouvons pas. Ce que nous pouvons faire, c'est créer une classe anonyme (c'est le troisième fichier) et l'instancier.


Et une instanciation de super classe?

La super classe abstraite n'est pas instanciée par nous mais par java.

EDIT: lui demander de tester cela

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

}

la sortie est:

false
class my$1
class my$2
ncenerar
la source
+1 pour l' observation 3: par exemple, nous pouvons faire Serializable s = new Serializable() {};( ce qui est assez inutile) et si marqué sur votre code donnerait class my$3(ou tout ce qui entoure la classe et le nombre)
Réintégrer Monica - notmaynard
18

Vous pouvez simplement répondre, en une seule ligne

Non , vous ne pouvez jamais instance de classe abstraite

Mais, l'intervieweur n'est toujours pas d'accord, alors vous pouvez lui dire

tout ce que vous pouvez faire est de créer une classe anonyme.

Et, selon la classe Anonymous, la classe déclarée et instanciée au même endroit / ligne

Il est donc possible que l'intervieweur s'intéresse à vérifier votre niveau de confiance et vos connaissances sur les POO.


la source
17

La partie technique a été bien couverte dans les autres réponses, et elle se termine principalement par:
"Il a tort, il ne sait rien, demandez-lui de rejoindre SO et de tout effacer :)"

Je voudrais aborder le fait (qui a été mentionné dans d'autres réponses) que cela pourrait être une question de stress et est un outil important pour de nombreux enquêteurs pour en savoir plus sur vous et comment réagissez-vous aux situations difficiles et inhabituelles. En vous donnant des codes incorrects, il voulait probablement voir si vous vous disputiez. Pour savoir si vous avez la confiance nécessaire pour vous opposer à vos aînés dans des situations similaires à celle-ci.

PS: Je ne sais pas pourquoi mais j'ai le sentiment que l'intervieweur a lu ce post.

Mixcels
la source
13

Les classes abstraites ne peuvent pas être instanciées, mais elles peuvent être sous-classées. Voir ce lien

Le meilleur exemple est

Bien que la classe Calender ait une méthode abstraite getInstance () , mais quand vous ditesCalendar calc=Calendar.getInstance();

calc fait référence à l'instance de classe de la classe GregorianCalendar comme "GregorianCalendar étend le calendrier "

En fait, le type interne annonymous vous permet de créer une sous-classe sans nom de la classe abstraite et une instance de celle-ci.

Abhishek_Mishra
la source
11

Réponse technique

Les classes abstraites ne peuvent pas être instanciées - c'est par définition et par conception.

Du JLS, Chapitre 8. Classes:

Une classe nommée peut être déclarée abstraite (§8.1.1.1) et doit être déclarée abstraite si elle n'est pas complètement implémentée; une telle classe ne peut pas être instanciée, mais peut être étendue par des sous-classes.

Depuis JSE 6 java doc pour Classes.newInstance ():

InstantiationException - si cette classe représente une classe abstraite, une interface, une classe de tableau, un type primitif ou void; ou si la classe n'a pas de constructeur nul; ou si l'instanciation échoue pour une autre raison.

Vous pouvez, bien sûr, instancier une sous-classe concrète d'une classe abstraite (y compris une sous-classe anonyme) et également effectuer une transtypage d'une référence d'objet en un type abstrait.

Un angle différent à ce sujet - Teamplay & Social Intelligence:

Ce type de malentendu technique se produit fréquemment dans le monde réel lorsque nous traitons de technologies complexes et de spécifications légalistes.

Les «compétences humaines» peuvent être plus importantes ici que les «compétences techniques». Si vous essayez de manière compétitive et agressive de prouver votre côté de l'argument, alors vous pourriez avoir théoriquement raison, mais vous pourriez également faire plus de dégâts en vous battant / en endommageant le "visage" / en créant un ennemi que cela ne vaut. Soyez réconciliant et compréhensif pour résoudre vos différends. Qui sait - peut-être que vous avez "tous les deux raison" mais que vous travaillez avec des significations légèrement différentes pour les termes ??

Qui sait - mais peu probable, il est possible que l'intervieweur ait délibérément introduit un petit conflit / malentendu pour vous mettre dans une situation difficile et voir comment vous vous comportez émotionnellement et socialement. Soyez courtois et constructif avec vos collègues, suivez les conseils des personnes âgées et poursuivez après l'entretien pour résoudre tout problème / malentendu - par e-mail ou appel téléphonique. Montre que vous êtes motivé et soucieux du détail.

Glen Best
la source
7

C'est un fait bien établi qui neabstract class peut pas être instancié comme tout le monde a répondu.

Lorsque le programme définit une classe anonyme, le compilateur crée en fait une nouvelle classe avec un nom différent (a le modèle EnclosedClassName$nnest le numéro de classe anonyme)

Donc, si vous décompilez cette classe Java, vous trouverez le code ci-dessous:

Ma classe

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

poly $ 1.class (la classe générée de la "classe anonyme")

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
la source
4

Non, vous ne pouvez pas instancier une classe abstraite. Nous instancions uniquement la classe anonyme. Dans la classe abstraite, nous déclarons des méthodes abstraites et définissons uniquement des méthodes concrètes.

vikas agrahari
la source
4

À propos des classes abstraites

  • Impossible de créer un objet d'une classe abstraite
  • Peut créer des variables (peut se comporter comme des types de données)
  • Si un enfant ne peut pas remplacer au moins une méthode abstraite du parent, alors l'enfant devient également abstrait
  • Les classes abstraites sont inutiles sans classes enfants

Le but d'une classe abstraite est de se comporter comme une base. Dans la hiérarchie d'héritage, vous verrez des classes abstraites vers le haut.

Priyankara
la source
3

Vous pouvez dire:
nous ne pouvons pas instancier une classe abstraite, mais nous pouvons utiliser un newmot clé pour créer une instance de classe anonyme en ajoutant simplement {}comme corps d'implémentation à la fin de la classe abstraite.

Tourbillon
la source
3

L'extension d'une classe ne signifie pas que vous instanciez la classe. En fait, dans votre cas, vous créez une instance de la sous-classe.

Je suis à peu près sûr que les classes abstraites ne permettent pas d'initier. Donc, je dirais non: vous ne pouvez pas instancier une classe abstraite. Mais, vous pouvez l'étendre / l'hériter.

Vous ne pouvez pas instancier directement une classe abstraite. Mais cela ne signifie pas que vous ne pouvez pas obtenir indirectement une instance de classe (pas réellement une instance de classe abstraite d'origine). Je veux dire que vous ne pouvez pas instancier la classe abstraite d'origine, mais vous pouvez:

  1. Créer une classe vide
  2. Héritez-le de la classe abstraite
  3. Instancier la classe dervied

Vous avez donc accès à toutes les méthodes et propriétés d'une classe abstraite via l'instance de classe dérivée.

Kas
la source
2

Il est impossible d'instancier une classe abstraite. Ce que vous pouvez vraiment faire, a implémenter certaines méthodes courantes dans une classe abstraite et laisser les autres non implémentées (en les déclarant abstraites) et laisser le descendant concret les implémenter en fonction de leurs besoins. Ensuite, vous pouvez créer une usine, qui retourne une instance de cette classe abstraite (en fait son implémenteur). En usine, vous décidez ensuite quel exécuteur choisir. Ceci est connu comme un modèle de conception d'usine:

   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'implémenteur concret n'a besoin que d'implémenter les méthodes déclarées comme abstraites, mais aura accès à la logique implémentée dans ces classes dans une classe abstraite, qui ne sont pas déclarées abstraites:

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

Enfin, l'usine ressemble à ceci:

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

Le récepteur de AbstractGridManager appellerait les méthodes sur lui et obtiendrait la logique, implémentée dans le descendeur concret (et partiellement dans les méthodes de classe abstraites) sans savoir quelle est l'implémentation concrète qu'il a obtenue. Ceci est également appelé inversion de contrôle ou injection de dépendance.

Picrochole
la source
2

Non, nous ne pouvons pas créer l'objet de classe abstraite, mais créer la variable de référence de la classe abstraite. La variable de référence est utilisée pour faire référence aux objets des classes dérivées (sous-classes de la classe abstraite)

Voici l'exemple qui illustre ce concept

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

Ici, nous voyons que nous ne pouvons pas créer l'objet de type Figure mais nous pouvons créer une variable de référence de type Figure. Ici, nous avons créé une variable de référence de type Figure et Figure Variable de référence de classe est utilisée pour faire référence aux objets de classe Rectangle et Triangle.

Ketan G
la source
0

En fait, nous ne pouvons pas créer directement un objet d'une classe abstraite. Ce que nous créons est une variable de référence d'un appel abstrait. La variable de référence est utilisée pour faire référence à l'objet de la classe qui hérite de la classe abstraite, c'est-à-dire la sous-classe de la classe abstraite.

Jency
la source