摘要 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,并且它是抽象的,因此它可以而且必须拒绝代码编译时间。
编译器知道 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,并且它是抽象的,因此它可以而且必须拒绝代码编译时间。