简体   繁体   English

抽象类:为什么newInstance()没有给出编译错误但构造函数调用给出了错误?

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

Compiler knows that AbstractDemo is an abstract class and Abstract classes can't be instantiated. 编译器知道AbstractDemo是一个抽象类,而Abstract类无法实例化。

But when I call newInstance() method, why it did not give a compile time error? 但是当我调用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();
        }
    }
}

Output when I ran this program: ( I know that error will come since I can't create instance for abstract class. But why it was not given at compile time is surprising me) 我运行这个程序时的输出:(我知道错误将会出现,因为我无法为抽象类创建实例。但是为什么在编译时没有给出它让我感到惊讶)

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)

EDIT: 编辑:

Compiler is intelligent to give error for this statement: 编译器很聪明地为此语句提供错误:

AbstractDemo demo2 = new AbstractDemo(); 

But not for this statement 但不是这个说法

AbstractDemo demo = AbstractDemo.class.newInstance(); 

Am I missing any key lesson here? 我错过了这里的重要课吗?

The compiler's job is to check compile-time rules (er, and compile the code). 编译器的工作是检查编译时规则(呃,编译代码)。 The method you're calling is Class#newInstance , not anything related (directly) to AbstractDemo . 您调用的方法是Class#newInstance ,而不是与AbstractDemo直接相关的任何AbstractDemo The fact that Class#newInstance will throw (because the instance of Class on which you call it is for an abstract class) is a runtime concern. Class#newInstance将抛出的事实(因为您调用它的Class的实例是抽象类)是运行时关注的问题。

While it might, in theory, be possible sometimes to determine at compile-time that a particular reference to a particular instance of Class refers to an abstract class (such as AbstractDemo.class ), usually it won't be possible, for instance: 理论上, 有时可能在编译时确定对Class的特定实例的特定引用是指抽象类(例如AbstractDemo.class ),通常它是不可能的,例如:

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

And even if it were, then we'd need some kind of built-in rule or annotation system (eg, compile-time information) saying "This method of this class can't be called if the Class instance refers to an abstract class." 即使它是,那么我们需要某种内置规则或注释系统(例如,编译时信息)说“如果Class实例引用一个抽象类,则不能调用此类的此方法“。

So we're talking about non-trivial work, and there's no real value in doing that work, making it sometimes a compile-time error and other times a runtime error. 所以我们谈论的是非平凡的工作,并且在完成这项工作时没有实际价值,使其有时成为编译时错误,有时则成为运行时错误。

Consider: The compiler could also work out that this throws an NPE: 考虑一下:编译器也可以解决这个问题:

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

Or that the body of this loop will never be executed: 或者永远不会执行此循环的主体:

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

But we don't have it do so; 但我们没有这样做; those are runtime concerns. 那些是运行时问题。

The compiler has no static information about what class a given Constructor instance is associated with. 编译器没有关于给定Constructor实例与哪个类相关联的静态信息。 With your particular code, a smart enough compiler could figure it out, but because that analysis cannot be performed in general, and therefore cannot be relied upon, compilers are not required to perform it. 使用您的特定代码,足够聪明的编译器可以解决它,但由于该分析通常无法执行,因此无法依赖,因此编译器不需要执行它。 I don't know any that do perform it. 我不知道有没有做过。

Note, however, that the newInstance() invocation will fail at run time , by throwing an InstantiationException . 但请注意, newInstance()调用将在运行时失败,方法是抛出InstantiationException That is, in fact, the sole and express purpose of that exception class. 实际上,这是该异常类的唯一和明确目的。

When you attempt to instantiate an abstract class directly, however, the compiler knows exactly what class you are instantiating, and that it is abstract, so it can and must reject the code at compile time. 但是,当您尝试直接实例化抽象类时,编译器确切地知道您要实例化的类,并且它是抽象的,因此它可以而且必须在编译时拒绝该代码。

The newInstance() method exists on the Class class. newInstance()方法存在于Class类中。 Since AbstractDemo.class is just an instance of class (and not a subclass of Class ), there is no way for the compiler to enforce this. 由于AbstractDemo.class只是类的一个实例(而不是Class的子Class ),因此编译器无法强制执行此操作。 The thing to know here is that when you use reflection, you are metaprogramming and can't really count on the compiler for much. 这里要知道的是,当你使用反射时,你是元编程,并不能真正依赖编译器。

To point this out a different way, cosider this code: 为了指出这个不同的方式,cosider这段代码:

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

Short of running the code, there is no way to know what the clazz variable could contain when newInstance() is called. 如果没有运行代码,那么在调用newInstance()时无法知道clazz变量可以包含什么。 So there is no way for the compiler to flag this. 所以编译器无法标记这一点。

You are asking to create a class at runtime not at compile time. 您要求在运行时创建一个类而不是在编译时。

The newInstance method will throw an InstantiationException at runtime in this case. 在这种情况下, newInstance方法将在运行时抛出InstantiationException

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

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