【发布时间】:2012-12-25 07:41:13
【问题描述】:
这个问题是关于 Java 的有趣行为:它产生 某些嵌套类的附加(非默认)构造函数 情况。
这个问题也是关于奇怪的匿名类,Java 用那个奇怪的构造函数产生。
考虑以下代码:
package a;
import java.lang.reflect.Constructor;
public class TestNested {
class A {
A() {
}
A(int a) {
}
}
public static void main(String[] args) {
Class<A> aClass = A.class;
for (Constructor c : aClass.getDeclaredConstructors()) {
System.out.println(c);
}
}
}
这将打印:
a.TestNested$A(a.TestNested)
a.TestNested$A(a.TestNested,int)
好的。接下来,让构造函数A(int a)私有:
private A(int a) {
}
再次运行程序。接收:
a.TestNested$A(a.TestNested)
private a.TestNested$A(a.TestNested,int)
也可以。但是现在,让我们以这种方式修改main() 方法(添加类A 创建的新实例):
public static void main(String[] args) {
Class<A> aClass = A.class;
for (Constructor c : aClass.getDeclaredConstructors()) {
System.out.println(c);
}
A a = new TestNested().new A(123); // new line of code
}
然后输入变成:
a.TestNested$A(a.TestNested)
private a.TestNested$A(a.TestNested,int)
a.TestNested$A(a.TestNested,int,a.TestNested$1)
它是什么:a.TestNested$A(a.TestNested,int,a.TestNested$1)
好的,让我们再次将构造函数 A(int a) 包本地化:
A(int a) {
}
再次重新运行程序(我们不要删除带有A创建实例的行!),输出与第一次一样:
a.TestNested$A(a.TestNested)
a.TestNested$A(a.TestNested,int)
问题:
1)如何解释?
2)第三个奇怪的构造函数是什么?
更新:调查显示如下。
1) 让我们尝试使用其他类的反射来调用这个奇怪的构造函数。
我们将无法做到这一点,因为没有任何方法可以创建那个奇怪的 TestNested$1 类的实例。
2) 好的。让我们做的伎俩。让我们在 TestNested 类中添加这样的静态字段:
public static Object object = new Object() {
public void print() {
System.out.println("sss");
}
};
嗯?好的,现在我们可以从另一个类中调用第三个奇怪的构造函数了:
TestNested tn = new TestNested();
TestNested.A a = (TestNested.A)TestNested.A.class.getDeclaredConstructors()[2].newInstance(tn, 123, TestNested.object);
对不起,我完全不明白。
UPDATE-2:其他问题是:
3) 为什么 Java 使用特殊的匿名内部类作为第三个合成构造函数的参数类型?为什么不只是 Object 类型,具有特殊名称的构造函数?
4) 哪些 Java 可以使用已经定义的匿名内部类来实现这些目的?这不是某种违反安全的行为吗?
【问题讨论】:
-
这是一个匿名的 A 内部类,它被传递给第三个构造函数。问题是它为什么在那里或里面有什么。想反编译吗?
-
@JanDvorak 这是什么原因? Java 可以做得更简单,只需修改构造函数的可访问状态。
-
但是它可以从外部访问......
-
@Andremoniy 响应您的第二次更新:Java 不能只使用
Object作为参数类型,因为您可以拥有自己的带有Object参数的构造函数并在没有参数的情况下调用它反射(通过使用非私有的A(int, Object)构造函数编译A,将TestNested$A.class文件保存在其他位置,删除新的构造函数,重新编译TestNested,并将新的TestNested$A.class替换为具有@987654346 的版本@构造函数。)使用匿名类型(TestNested$1)作为合成构造函数的参数类型可以防止这种情况发生。 -
@Andremoniy 和关于违反安全性:不可能,因为构造函数的参数类型是在编译时绑定的(当你重新编译外部类时,它也会重新编译内部类)。即使你有一个与匿名类型相同类型的对象,你也不能有一个以该类型作为参数的构造函数,所以编译器会选择一个不同的构造函数来调用。
标签: java reflection nested-class