摘要 class : 为什么 newInstance() 没有给出编译错误但构造函数调用给出错误?

Abstract class : Why newInstance() is not giving compilation error but constructor call gives error?

编译器知道 AbstractDemo 是一个抽象 class 并且抽象 classes 不能被实例化。

但是当我调用newInstance()方法时,为什么没有给出编译时错误?

import java.lang.reflect.Constructor;

public abstract class AbstractDemo{
    public AbstractDemo(){
        System.out.println("Default constructor");
    }
    public static void main(String args[]){
        try{
            /* No compilation error for this statement */
            AbstractDemo demo = AbstractDemo.class.newInstance(); 

            Constructor[] ctors = AbstractDemo.class.getDeclaredConstructors();
            for ( int i=0; i < ctors.length; i++){
                System.out.println(ctors[i]);
                /* No compilation error for this statement too */
                AbstractDemo demo1 = (AbstractDemo) ctors[i].newInstance();
            }
            /* Compilation error here */
            // AbstractDemo demo2 = new AbstractDemo(); 
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

当我 运行 这个程序时的输出:(我知道会出现错误,因为我无法为抽象 class 创建实例。但是为什么在编译时没有给出它让我感到惊讶)

D:\Study\Java>java AbstractDemo

java.lang.InstantiationException
        at sun.reflect.InstantiationExceptionConstructorAccessorImpl.newInstance(InstantiationExceptionConstructorAccessorImpl.java:48)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
        at java.lang.Class.newInstance(Class.java:374)
        at AbstractDemo.main(AbstractDemo.java:10)

编辑:

编译器很聪明地给出了这条语句的错误:

AbstractDemo demo2 = new AbstractDemo(); 

但不是针对此声明

AbstractDemo demo = AbstractDemo.class.newInstance(); 

我是否遗漏了任何关键课程?

newInstance() 方法存在于 Class class 上。由于 AbstractDemo.class 只是 class 的一个实例(而不是 Class 的子 class),编译器无法强制执行此操作。这里要知道的是,当你使用反射时,你是在进行元编程,不能真正依赖编译器。

要以不同的方式指出这一点,请考虑以下代码:

Class clazz = AbstractDemo.class;
// code that could possibly re-assign clazz here
Object o = clazz.newInstance();

缺少 运行 代码,无法知道调用 newInstance()clazz 变量可能包含什么。所以编译器无法标记这一点。

您要求在运行时而非编译时创建 class。

在这种情况下,newInstance 方法将在运行时抛出一个 InstantiationException

编译器的工作是检查compile-time 规则(呃,编译代码)。您调用的方法是 Class#newInstance,与 AbstractDemo 没有任何(直接)相关的内容。 Class#newInstance 将抛出的事实(因为您调用它的 Class 的实例是抽象 class)是一个运行时问题。

虽然理论上 有时 可以确定 compile-time 对 Class 的特定实例的特定引用是指abstract class(比如AbstractDemo.class),一般是不可能的,比如:

void someMethodInMyOwnClass(Class c) {
    Object o = c.newInstance();
}

即使是,我们也需要某种 built-in 规则或注释系统(例如,compile-time 信息)说 "This method of this class can't be called if the Class instance refers to an abstract class."

所以我们谈论的是 non-trivial 工作,做这项工作没有真正的价值,有时会导致 compile-time 错误,有时会导致运行时错误。

考虑:编译器也可以算出这会抛出 NPE:

String s = null;
if (s.equalsIgnoreCase("foo")) {
    // ...
}

或者永远不会执行此循环体:

int x = 10;
while (x < 10) {
    System.out.println("Never gets here");
}

但我们没有这样做;这些是运行时问题。

编译器没有关于给定 Constructor 实例与什么 class 关联的静态信息。对于您的特定代码,足够聪明的编译器可以解决它,但是由于一般无法执行该分析,因此不能依赖,因此不需要编译器来执行它。我不知道有谁会执行它。

但是请注意,newInstance() 调用将在 运行 时间 失败 ,方法是抛出 InstantiationException。也就是说,事实上,该例外的唯一明确目的 class.

但是,当您尝试直接实例化抽象 class 时,编译器确切地知道您正在实例化什么 class,并且它是抽象的,因此它可以而且必须拒绝代码编译时间。