编译器知道这AbstractDemo
是一个抽象类,而抽象类无法实例化.
但是当我调用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(); } } }
我运行这个程序时的输出:(我知道错误会出现,因为我无法为抽象类创建实例.但是为什么在编译时没有给出它让我感到惊讶)
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();
我错过了这里的重要课吗?
编译器的工作是检查编译时规则(呃,编译代码).你正在调用的方法Class#newInstance
,而不是(直接)与之相关的任何方法AbstractDemo
.Class#newInstance
将抛出的事实(因为Class
您将其称为抽象类的实例)是运行时关注的事实.
理论上,有时可能在编译时确定对特定实例的特定引用是Class
指抽象类(例如AbstractDemo.class
),通常它是不可能的,例如:
void someMethodInMyOwnClass(Class c) { Object o = c.newInstance(); }
即使它是,那么我们需要某种内置规则或注释系统(例如,编译时信息)说"如果Class
实例引用抽象类,则不能调用此类的此方法. "
所以我们谈论的是非平凡的工作,并且在完成这项工作时没有实际价值,使其有时成为编译时错误,有时则成为运行时错误.
考虑一下:编译器也可以解决这个问题:
String s = null; if (s.equalsIgnoreCase("foo")) { // ... }
或者永远不会执行此循环的主体:
int x = 10; while (x < 10) { System.out.println("Never gets here"); }
但我们没有这样做; 那些是运行时问题.