为什么在访问最终局部变量时,为什么在Java中具有这种实例化感应?
-
04-10-2019 - |
题
我正在玩一些代码来制作“关闭”构造(不工作的btw)
一切看起来都很好,但是当我尝试访问代码中的最终局部变量时,例外 InstantiationException
被扔了。
如果我通过将其完全删除或通过使其类别属性删除对本地变量的访问,则不会发生异常。
当应用程序试图使用newInstance方法中的类别创建类实例时,请投掷,但是不能实例化指定的类对象。实例化可能因多种原因而失败,包括但不限于:
- 类对象代表一个抽象类,接口,数组类,原始类型或void
- 课程没有零构造函数
其他原因可能引起了这个问题?
这是代码。注释 /不输入类别属性 /本地变量以查看效果(行:5和10)。
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
class InstantiationExceptionDemo {
//static JTextField field = new JTextField();// works if uncommented
public static void main( String [] args ) {
JFrame frame = new JFrame();
JButton button = new JButton("Click");
final JTextField field = new JTextField();// fails if uncommented
button.addActionListener( new _(){{
System.out.println("click " + field.getText());
}});
frame.add( field );
frame.add( button, BorderLayout.SOUTH );
frame.pack();frame.setVisible( true );
}
}
class _ implements ActionListener {
public void actionPerformed( ActionEvent e ){
try {
this.getClass().newInstance();
} catch( InstantiationException ie ){
throw new RuntimeException( ie );
} catch( IllegalAccessException ie ){
throw new RuntimeException( ie );
}
}
}
这是Java中的错误吗?
编辑
哦,我忘记了,堆栈(扔掉时)是:
Caused by: java.lang.InstantiationException: InstantiationExceptionDemo$1
at java.lang.Class.newInstance0(Class.java:340)
at java.lang.Class.newInstance(Class.java:308)
at _.actionPerformed(InstantiationExceptionDemo.java:25)
解决方案
好吧,这很有意义。
仅您的第一个实例 _
类可以访问本地变量。随后的实例不能,除非您为其提供(通过构造函数ARG)
Constructor[] constructor = a.getClass().getDeclaredConstructors();
for (Constructor c : constructors) {
System.out.println(c.getParameterTypes().length);
}
输出1.((a
是您匿名类的实例)
就是说,我认为这不是实施封闭的好方法。初始化块至少一次调用一次,而无需它。我认为您只是在玩耍,但请看一下 Lambdaj. 。或等待Java 7 :)
其他提示
这是 javap -c InstantiationExceptionDemo$1
的 static field
版本:
Compiled from "InstantiationExceptionDemo.java"
class InstantiationExceptionDemo$1 extends _{
InstantiationExceptionDemo$1();
Code:
0: aload_0
1: invokespecial #8; //Method _."<init>":()V
4: getstatic #10; //Field InstantiationExceptionDemo.field:
//Ljavax/swing/JTextField;
这是 javap -c InstantiationExceptionDemo$1
的 final
本地变量版本:
Compiled from "InstantiationExceptionDemo.java"
class InstantiationExceptionDemo$1 extends _{
InstantiationExceptionDemo$1(javax.swing.JTextField);
Code:
0: aload_0
1: invokespecial #8; //Method _."<init>":()V
4: aload_1
所以有你的原因: final
本地变量版本需要一个额外的参数, JTextField
参考,在构造函数中。它没有零构造器。
如果您考虑一下,这是有道理的。否则,这个版本如何 InstantiationExceptionDemo$1
要得到 field
参考?编译器隐藏了以下事实:这是合成构造器的参数。
感谢Bozho和Polygenlubricants的启发性答案。
因此,原因是(用我自己的话)
当使用本地最终变量时,编译器将使用匿名内部类使用的字段创建构造函数并调用它。它也“注入”字段具有值。
因此,我所做的是修改我的创建,以使用反射为正确的构造函数加载正确的构造函数。
这是结果的代码:
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
import java.lang.reflect.*;
class InstantiationExceptionDemo {
public static void main( String [] args ) {
JFrame frame = new JFrame();
final JButton reverse = new JButton("Reverse");
final JButton swap = new JButton("Swap");
final JTextField fieldOne = new JTextField(20);
final JTextField fieldTwo = new JTextField(20);
// reverse the string in field one
reverse.addActionListener( new _(){{
StringBuilder toReverse = new StringBuilder();
toReverse.append( fieldOne.getText() );
toReverse.reverse();
fieldOne.setText( toReverse.toString() );
//fieldOne.setText( new StringBuilder( fieldOne.getText() ).reverse().toString() );
}});
// swap the fields
swap.addActionListener( new _(){{
String temp = fieldOne.getText();
fieldOne.setText( fieldTwo.getText() );
fieldTwo.setText( temp );
}});
// scaffolding
frame.add( new JPanel(){{
add( fieldOne );
add( fieldTwo );
}} );
frame.add( new JPanel(){{
add( reverse );
add( swap );
}}, BorderLayout.SOUTH );
frame.pack();frame.setVisible( true );
}
}
abstract class _ implements ActionListener {
public _(){}
public void actionPerformed( ActionEvent e ){
invokeBlock();
}
private void invokeBlock(){
// does actually invoke the block but with a trick
// it creates another instance of this same class
// which will be immediately discarded because there are no more
// references to it.
try {
// fields declared by the compiler in the anonymous inner class
Field[] fields = this.getClass().getDeclaredFields();
Class[] types= new Class[fields.length];
Object[] values = new Object[fields.length];
int i = 0;
for( Field f : fields ){
types[i] = f.getType();
values[i] = f.get( this );
i++;
}
// this constructor was added by the compiler
Constructor constructor = getClass().getDeclaredConstructor( types );
constructor.newInstance( values );
} catch( InstantiationException ie ){
throw new RuntimeException( ie );
} catch( IllegalAccessException ie ){
throw new RuntimeException( ie );
}catch( InvocationTargetException ie ){
throw new RuntimeException( ie );
} catch(NoSuchMethodException nsme){
throw new RuntimeException( nsme );
}
}
}
当然,正如Bozho指出的那样,这不是一个好方法(不是一种方式,但不是一个好方法)。
这有两个问题。
1.-声明时调用初始块。
2.-无法获得实际代码的参数(即ActionEevent在Action Performed中)
如果我们只能延迟初始化器块的执行,这将使(根据语法)闭合替代方案。
也许在Java 7 :(