简体   繁体   English

我们可以实例化一个抽象类吗?

[英]Can we instantiate an abstract class?

During one of my interview, I was asked "If we can instantiate an abstract class?"在我的一次采访中,有人问我“我们是否可以实例化一个抽象类?”

My reply was "No. we can't".我的回答是“不,我们不能”。 But, interviewer told me "Wrong, we can."但是,面试官告诉我“错了,我们可以。”

I argued a bit on this.对此我有点争论。 Then he told me to try this myself at home.然后他告诉我自己在家试试这个。

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

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

Here, I'm creating instance of my class and calling method of abstract class.在这里,我正在创建我的类的实例并调用抽象类的方法。 Can anyone please explain this to me?任何人都可以向我解释一下吗? Was I really wrong during my interview?我面试的时候真的错了吗?

Here, i'm creating instance of my class在这里,我正在创建我的类的实例

No, you are not creating the instance of your abstract class here.不,您不是在这里创建抽象类的实例。 Rather you are creating an instance of an anonymous subclass of your abstract class.相反,您正在创建抽象类的匿名子类的实例。 And then you are invoking the method on your abstract class reference pointing to subclass object .然后您在指向子类 object 的抽象类引用上调用该方法。

This behaviour is clearly listed in JLS - Section # 15.9.1 : - JLS - 第 15.9.1 节中明确列出了此行为:-

If the class instance creation expression ends in a class body, then the class being instantiated is an anonymous class.如果类实例创建表达式以类体结尾,则被实例化的类是匿名类。 Then:然后:

  • If T denotes a class, then an anonymous direct subclass of the class named by T is declared.如果 T 表示一个类,则声明由 T 命名的类的匿名直接子类。 It is a compile-time error if the class denoted by T is a final class.如果由 T 表示的类是最终类,则会出现编译时错误。
  • If T denotes an interface, then an anonymous direct subclass of Object that implements the interface named by T is declared.如果 T 表示一个接口,则声明一个实现由 T 命名的接口的 Object 的匿名直接子类。
  • In either case, the body of the subclass is the ClassBody given in the class instance creation expression.在任何一种情况下,子类的主体都是类实例创建表达式中给出的 ClassBody。
  • The class being instantiated is the anonymous subclass.被实例化的类是匿名子类。

Emphasis mine.强调我的。

Also, in JLS - Section # 12.5 , you can read about the Object Creation Process .此外,在JLS - 第 12.5 节中,您可以阅读有关对象创建过程的信息 I'll quote one statement from that here: -我将在此处引用其中的一项声明:-

Whenever a new class instance is created, memory space is allocated for it with room for all the instance variables declared in the class type and all the instance variables declared in each superclass of the class type, including all the instance variables that may be hidden.每当创建新的类实例时,都会为其分配内存空间,为类类型中声明的所有实例变量和类类型的每个超类中声明的所有实例变量(包括所有可能隐藏的实例变量)分配空间。

Just before a reference to the newly created object is returned as the result, the indicated constructor is processed to initialize the new object using the following procedure:就在对新创建的对象的引用作为结果返回之前,指示的构造函数被处理以使用以下过程初始化新对象:

You can read about the complete procedure on the link I provided.您可以在我提供的链接上阅读完整的程序。


To practically see that the class being instantiated is an Anonymous SubClass , you just need to compile both your classes.要实际看到被实例化的类是Anonymous SubClass ,您只需要编译您的两个类。 Suppose you put those classes in two different files:假设您将这些类放在两个不同的文件中:

My.java:我的.java:

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

Poly.java: Poly.java:

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

Now, compile both your source files:现在,编译两个源文件:

javac My.java Poly.java

Now in the directory where you compiled the source code, you will see the following class files:现在在您编译源代码的目录中,您将看到以下类文件:

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

See that class - Poly$1.class .看到那个类 - Poly$1.class It's the class file created by the compiler corresponding to the anonymous subclass you instantiated using the below code:它是由编译器创建的类文件,对应于您使用以下代码实例化的匿名子类:

new My() {};

So, it's clear that there is a different class being instantiated.所以,很明显,有一个不同的类正在被实例化。 It's just that, that class is given a name only after compilation by the compiler.只是,那个类只有在编译器编译后才给它一个名字。

In general, all the anonymous subclasses in your class will be named in this fashion:通常,类中的所有匿名子类都将以这种方式命名:

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

Those numbers denote the order in which those anonymous classes appear in the enclosing class.这些数字表示这些匿名类在封闭类中出现的顺序。

The above instantiates an anonymous inner class which is a subclass of the my abstract class.上面实例化了一个匿名内部类,它是my抽象类的子类。 It's not strictly equivalent to instantiating the abstract class itself.它并不严格等同于实例化抽象类本身。 OTOH, every subclass instance is an instance of all its super classes and interfaces, so most abstract classes are indeed instantiated by instantiating one of their concrete subclasses. OTOH,每个子类实例都是其所有超类和接口的实例,因此大多数抽象类确实通过实例化其具体子类之一来实例化。

If the interviewer just said "wrong!"如果面试官只是说“错了!” without explaining, and gave this example, as a unique counterexample, I think he doesn't know what he's talking about, though.没有解释,并举了这个例子,作为一个独特的反例,我认为他不知道他在说什么,虽然。

= my() {}; means that there's an anonymous implementation, not simple instantiation of an object, which should have been : = my() .意味着有一个匿名实现,而不是对象的简单实例化,它应该是 : = my() You can never instantiate an abstract class.你永远不能实例化一个抽象类。

Just observations you could make:你可以做的只是观察:

  1. Why poly extends my ?为什么poly扩展了my ? This is useless...这是没用的...
  2. What is the result of the compilation?编译的结果是什么? Three files: my.class , poly.class and poly$1.class三个文件: my.classpoly.classpoly$1.class
  3. If we can instantiate an abstract class like that, we can instantiate an interface too... weird...如果我们可以像这样实例化一个抽象类,我们也可以实例化一个接口......奇怪......


Can we instantiate an abstract class?我们可以实例化一个抽象类吗?

No, we can't.不,我们不能。 What we can do is, create an anonymous class (that's the third file) and instantiate it.我们可以做的是,创建一个匿名类(即第三个文件)并实例化它。


What about a super class instantiation?超类实例化怎么样?

The abstract super class is not instantiated by us but by java.抽象超类不是我们实例化的,而是java实例化的。

EDIT: Ask him to test this编辑:让他测试这个

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

}

output is:输出是:

false
class my$1
class my$2

You can simply answers, in just one line您可以简单地回答,只需一行

No , you can never instance Abstract Class,你永远不能实例抽象类

But, interviewer still not agree, then you can tell him/her但是,面试官还是不同意,那你可以告诉他/她

all you can do is, you can create an Anonymous Class.你所能做的就是,你可以创建一个匿名类。

And, according to Anonymous class, class declared and instantiate at the same place/line并且,根据匿名类,类在同一个地方/行声明和实例化

So, it might be possible that, interviewer would be interested to check your confidence level and how much you know about the OOPs .因此,面试官可能有兴趣检查您的信心水平以及您对 OOP 的了解程度。

The technical part has been well-covered in the other answers, and it mainly ends in:技术部分在其他答案中已经有很好的介绍,主要结束于:
"He is wrong, he doesn't know stuff, ask him to join SO and get it all cleared :)" “他错了,他什么都不知道,请他加入 SO 并将其全部清除:)”

I would like to address the fact(which has been mentioned in other answers) that this might be a stress-question and is an important tool for many interviewers to know more about you and how do you react to difficult and unusual situations.我想说明一个事实(在其他答案中已经提到),这可能是一个压力问题,并且是许多面试官更多地了解您以及您如何应对困难和异常情况的重要工具。 By giving you incorrect codes, he probably wanted to see if you argued back.通过给你不正确的代码,他可能想看看你是否反驳。 To know whether you have the confidence to stand up against your seniors in situations similar to this.了解您是否有信心在类似情况下与前辈对抗。

PS: I don't know why but I have a feeling that the interviewer has read this post. PS:不知道为什么,感觉面试官看了这篇文章。

Abstract classes cannot be instantiated, but they can be subclassed.抽象类不能被实例化,但它们可以被子类化。 See This Link 看到这个链接

The best example is最好的例子是

Although Calender class has a abstract method getInstance() , but when you say Calendar calc=Calendar.getInstance();虽然Calender 类有一个抽象方法 getInstance() ,但是当你说Calendar calc=Calendar.getInstance();

calc is referring to the class instance of class GregorianCalendar as "GregorianCalendar extends Calendar " calc 将类 GregorianCalendar 的类实例称为“GregorianCalendar extends Calendar

Infact annonymous inner type allows you to create a no-name subclass of the abstract class and an instance of this.事实上,匿名内部类型允许您创建抽象类的无名子类和 this 的实例。

Technical Answer技术解答

Abstract classes cannot be instantiated - this is by definition and design.抽象类不能被实例化——这是由定义和设计决定的。

From the JLS, Chapter 8. Classes:来自 JLS,第 8 章。类:

A named class may be declared abstract (§8.1.1.1) and must be declared abstract if it is incompletely implemented;命名类可以声明为抽象类(第 8.1.1.1 节),如果未完全实现,则必须声明为抽象类; such a class cannot be instantiated, but can be extended by subclasses.这样的类不能被实例化,但可以由子类扩展。

From JSE 6 java doc for Classes.newInstance():来自 Classes.newInstance() 的 JSE 6 java 文档:

InstantiationException - if this Class represents an abstract class, an interface, an array class, a primitive type, or void; InstantiationException - 如果这个 Class 表示一个抽象类、一个接口、一个数组类、一个基本类型或 void; or if the class has no nullary constructor;或者如果该类没有空构造函数; or if the instantiation fails for some other reason.或者如果实例化由于其他原因失败。

You can, of course, instantiate a concrete subclass of an abstract class (including an anonymous subclass) and also carry out a typecast of an object reference to an abstract type.当然,您可以实例化抽象类(包括匿名子类)的具体子类,也可以执行对抽象类型的对象引用的类型转换。

A Different Angle On This - Teamplay & Social Intelligence:一个不同的角度 - 团队合作和社交智能:

This sort of technical misunderstanding happens frequently in the real world when we deal with complex technologies and legalistic specifications.当我们处理复杂的技术和法律规范时,这种技术误解在现实世界中经常发生。

"People Skills" can be more important here than "Technical Skills".在这里,“人员技能”可能比“技术技能”更重要。 If competitively and aggressively trying to prove your side of the argument, then you could be theoretically right, but you could also do more damage in having a fight / damaging "face" / creating an enemy than it is worth.如果竞争激烈地试图证明你的论点,那么理论上你可能是对的,但你也可能在打架/破坏“面子”/制造敌人时造成比其价值更多的伤害。 Be reconciliatory and understanding in resolving your differences.在解决分歧时要和解和理解。 Who knows - maybe you're "both right" but working off slightly different meanings for terms??谁知道——也许你“都是对的”,但术语的含义略有不同??

Who knows - though not likely, it is possible the interviewer deliberately introduced a small conflict/misunderstanding to put you into a challenging situation and see how you behave emotionally and socially.谁知道呢——虽然不太可能,但面试官有可能故意引入一个小冲突/误解,让你陷入一个充满挑战的境地,看看你在情感和社交上的表现。 Be gracious and constructive with colleagues, follow advice from seniors, and follow through after the interview to resolve any challenge/misunderstanding - via email or phone call.对同事要有礼貌和建设性,听从前辈的建议,并在面试后通过电子邮件或电话解决任何挑战/误解。 Shows you're motivated and detail-oriented.表明你有动力并且注重细节。

It is a well-established fact that abstract class can not be instantiated as everyone answered. abstract class不能像大家回答的那样被实例化,这是一个公认的事实。

When the program defines anonymous class, the compiler actually creates a new class with different name (has the pattern EnclosedClassName$n where n is the anonymous class number)当程序定义匿名类时,编译器实际上会创建一个不同名称的新类(具有模式EnclosedClassName$n其中n是匿名类号)

So if you decompile this Java class you will find the code as below:所以如果你反编译这个Java类,你会发现代码如下:

my.class我的课

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

poly$1.class (the generated class of the "anonymous class") poly$1.class(“匿名类”的生成类)

class poly$1 extends my 
{
} 

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

Extending a class doesn't mean that you are instantiating the class.扩展类并不意味着您正在实例化该类。 Actually, in your case you are creating an instance of the subclass.实际上,在您的情况下,您正在创建子类的实例。

I am pretty sure that abstract classes do not allow initiating.我很确定抽象类不允许启动。 So, I'd say no: you can't instantiate an abstract class.所以,我会说不:你不能实例化一个抽象类。 But, you can extend it / inherit it.但是,您可以扩展它/继承它。

You can't directly instantiate an abstract class.您不能直接实例化抽象类。 But it doesn't mean that you can't get an instance of class (not actully an instance of original abstract class) indirectly.但这并不意味着您不能间接获得类的实例(实际上不是原始抽象类的实例)。 I mean you can not instantiate the orginial abstract class, but you can:我的意思是你不能实例化原始抽象类,但你可以:

  1. Create an empty class创建一个空类
  2. Inherit it from abstract class从抽象类继承
  3. Instantiate the dervied class实例化派生类

So you get access to all the methods and properties in an abstract class via the derived class instance.因此,您可以通过派生类实例访问抽象类中的所有方法和属性。

About Abstract Classes关于抽象类

  • Cannot create object of an abstract class无法创建抽象类的对象
  • Can create variables (can behave like datatypes)可以创建变量(可以像数据类型一样)
  • If a child can not override at least of one abstract method of the parent, then child also becomes abstract如果一个孩子不能覆盖至少一个父的抽象方法,那么孩子也变成抽象的
  • Abstract classes are useless without child classes没有子类的抽象类是无用的

The purpose of an abstract class is to behave like a base.抽象类的目的是表现得像一个基类。 In inheritance hierarchy you will see abstract classes towards the top.在继承层次结构中,您将在顶部看到抽象类。

不,你不能实例化抽象类。我们只实例化匿名类。在抽象类中,我们只声明抽象方法并定义具体方法。

You can say:你可以说:
we can't instantiate an abstract class, but we can use new keyword to create an anonymous class instance by just adding {} as implement body at the the end of the abstract class.我们不能实例化一个抽象类,但是我们可以使用new关键字创建一个匿名类实例,只需在抽象类的末尾添加{}作为实现体。

It's impossible to instantiate an abstract class.实例化抽象类是不可能的。 What you really can do, has implement some common methods in an abstract class and let others unimplemented (declaring them abstract) and let the concrete descender implement them depending on their needs.你真正能做的是,在抽象类中实现一些通用方法,让其他人未实现(声明它们是抽象的),让具体的后代根据他们的需要实现它们。 Then you can make a factory, which returns an instance of this abstract class (actually his implementer).然后你可以创建一个工厂,它返回这个抽象类的一个实例(实际上是他的实现者)。 In the factory you then decide, which implementer to choose.然后在工厂中决定选择哪个实施者。 This is known as a factory design pattern:这被称为工厂设计模式:

   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
    }

The concrete implementer only needs to implement the methods declared as abstract, but will have access to the logic implemented in those classes in an abstract class, which are not declared abstract:具体实现者只需要实现声明为抽象的方法,但可以访问抽象类中那些类中实现的逻辑,这些类没有声明为抽象:

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

Then finally the factory looks something like this:最后工厂看起来像这样:

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

The receiver of AbstractGridManager would call the methods on him and get the logic, implemented in the concrete descender (and partially in the abstract class methods) without knowing what is the concrete implementation he got. AbstractGridManager 的接收者将调用他的方法并获取在具体后代中(部分在抽象类方法中)实现的逻辑,而不知道他得到的具体实现是什么。 This is also known as inversion of control or dependency injection.这也称为控制反转或依赖注入。

No, we can't create the object of abstract class, but create the reference variable of the abstract class.不,我们不能创建抽象类的对象,而是创建抽象类的引用变量。 The reference variable is used to refer to the objects of derived classes (Sub classes of Abstract class)引用变量用于引用派生类(抽象类的子类)的对象

Here is the example that illustrates this 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()); 
    } 
}

Here we see that we cannot create the object of type Figure but we can create a reference variable of type Figure.在这里我们看到我们不能创建 Figure 类型的对象,但我们可以创建一个 Figure 类型的引用变量。 Here we created a reference variable of type Figure and Figure Class reference variable is used to refer to the objects of Class Rectangle and Triangle.这里我们创建了一个Figure类型的引用变量,Figure类引用变量用于引用Rectangle类和Triangle类的对象。

Actually we can not create an object of an abstract class directly.实际上我们不能直接创建抽象类的对象。 What we create is a reference variable of an abstract call.我们创建的是一个抽象调用的引用变量。 The reference variable is used to Refer to the object of the class which inherits the Abstract class ie the subclass of the abstract class.引用变量用于引用继承抽象类的类的对象,即抽象类的子类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM