在我的一次面试中,有人问我:“我们是否可以实例化一个抽象类?”

我的回答是:“没有。我们不能”。但是,面试官告诉我:“错了,我们可以。”

我对此进行了一些争论。然后他让我自己在家试试。

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

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

在这里,我正在创建我的类的实例和调用抽象类的方法。有人能给我解释一下吗?我的面试真的错了吗?


当前回答

实例化一个抽象类是不可能的。 你真正能做的是,在一个抽象类中实现一些通用方法,而让其他方法未实现(将它们声明为抽象),并让具体的派生程序根据需要实现它们。 然后您可以创建一个工厂,该工厂返回这个抽象类的实例(实际上是他的实现者)。然后在工厂中决定选择哪个实现者。这被称为工厂设计模式:

   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
    }

具体实现者只需要实现声明为抽象的方法,但可以访问在抽象类中那些类中实现的逻辑,这些类不是声明为抽象的:

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

最后,工厂看起来是这样的:

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

AbstractGridManager的接收者将调用他身上的方法并获得在具体的下降器中实现的逻辑(部分在抽象类方法中实现),而不知道他得到的具体实现是什么。这也称为控制反转或依赖注入。

其他回答

关于抽象类

不能创建抽象类的对象 可以创建变量(可以像数据类型一样运行) 如果子对象不能重写父对象的至少一个抽象方法,那么子对象也将成为抽象对象 如果没有子类,抽象类是无用的

抽象类的目的是表现得像基类。在继承层次结构中,你会看到抽象类位于顶部。

= my() {};意味着有一个匿名的实现,而不是简单的对象实例化,它应该是:= my()。你永远不能实例化一个抽象类。

这里,我正在创建我的类的实例

不,您不是在这里创建抽象类的实例。相反,您是在创建抽象类的匿名子类的实例。然后在指向子类对象的抽象类引用上调用方法。

这种行为在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. It is a compile-time error if the class denoted by T is a final class. If T denotes an interface, then an anonymous direct subclass of Object that implements the interface named by T is declared. In either case, the body of the subclass is the ClassBody given in the class instance creation expression. The class being instantiated is the anonymous subclass.

我特别强调。

此外,在JLS -第12.5节中,您可以阅读到关于对象创建过程的内容。我在这里引用其中的一段话:-

每当创建一个新的类实例时,就分配内存空间 为该类中声明的所有实例变量留出空间 类的每个超类中声明的所有实例变量 类类型,包括可能隐藏的所有实例变量。 控件返回对新创建对象的引用之前 结果,则处理指定的构造函数以初始化new 对象,使用以下过程:

你可以在我提供的链接上阅读完整的程序。


要实际看到被实例化的类是一个匿名子类,您只需要编译两个类。假设你把这些类放在两个不同的文件中:

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

现在,编译两个源文件:

javac My.java Poly.java

现在在你编译源代码的目录中,你会看到以下类文件:

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

看那个类- Poly$1.class。它是编译器创建的类文件,对应于你使用下面的代码实例化的匿名子类:

new My() {};

很明显,有一个不同的类被实例化了。只是,这个类只有在编译器编译后才会被赋予名称。

一般来说,类中的所有匿名子类都将以这种方式命名:

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

这些数字表示这些匿名类在外围类中出现的顺序。

不,我们不能创建抽象类的对象,而是创建抽象类的引用变量。引用变量用于引用派生类(抽象类的子类)的对象。

下面的例子说明了这个概念

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

在这里,我们可以看到不能创建类型为Figure的对象,但可以创建类型为Figure的引用变量。这里我们创建了一个类型为Figure的引用变量,而Figure类引用变量用于引用Rectangle和Triangle类的对象。

技术答案

抽象类不能被实例化——这是通过定义和设计实现的。

摘自JLS,第8章。类:

命名类可以声明为抽象类(§8.1.1.1),并且必须声明 抽象,如果没有完全实现;这样的类是不可能的 实例化,但可以通过子类扩展。

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

如果这个类表示一个抽象类,一个接口,一个数组 Class,一个基本类型,或void;或者类没有null构造函数;或者如果 实例化失败是由于其他原因。

当然,您可以实例化抽象类的具体子类(包括匿名子类),也可以对抽象类型的对象引用进行类型转换。

团队合作与社交智商:

在现实世界中,当我们处理复杂的技术和法律规范时,这种技术误解经常发生。

在这里,“人际交往能力”可能比“技术能力”更重要。如果你试图证明自己的观点,那么理论上你可能是正确的,但你也可能在战斗中造成更大的伤害/损害“面子”/制造敌人。以和解和理解的方式解决你们的分歧。谁知道呢——也许你“都是对的”,但对术语的含义略有不同??

谁知道呢——虽然不太可能,但也有可能面试官故意引入一个小冲突/误解,把你置于一个具有挑战性的环境中,看看你在情感和社交方面的表现如何。对同事要有礼貌和建设性,听从前辈的建议,在面试结束后通过电子邮件或电话解决任何挑战/误解。这表明你很积极,注重细节。