我试图编写一个Java源代码的表达式或一系列语句,当在static
方法中写入时评估为null
,但如果该方法是非静态的,则求值为this
。
我最初的想法是过载' on static vs non-static,如下所示:
public class test {
public void method1() {
System.out.println(getThisOrNull());
}
public static void method2() {
System.out.println(getThisOrNull());
}
private static Object getThisOrNull() {
return null;
}
private Object getThisOrNull() {
return this;
}
public static void main(String[] args) {
test t = new test();
System.out.println(t);
t.method1();
t.method2();
}
}
不幸的是,这实际上不是合法的Java,你不能超载'像这样,它只是给出了编译器错误:
test.java:14: error: method getThisOrNull() is already defined in class test
private Object getThisOrNull() {
^
1 error
显然在一个理想的世界中我不会像那样开始编写它,但问题是这个代码将由一个工具自动生成,该工具在语义上或语法上都不足以区分静态与非静态 - 病例。
那么,我怎样才能编写一些源代码,尽管字节为字节相同,但根据方法的static
修饰符的存在而编译和行为不同?
答案 0 :(得分:3)
这可以通过Java的反射工具的技巧和一些帮助来实现。这很难看,但它确实有效:
import java.lang.reflect.Field;
public class test {
public void method1() {
System.out.println(getThisOrNull(new Object(){}));
}
public static void method2() {
System.out.println(getThisOrNull(new Object(){}));
}
private static Object getThisOrNull(final Object o) {
for (Field f: o.getClass().getDeclaredFields()) {
if (f.getType().equals(test.class)) {
try {
return f.get(o);
}
catch (IllegalAccessException e) {
// Omm nom nom...
}
}
}
return null;
}
public static void main(String[] args) {
test t = new test();
System.out.println(t);
t.method1();
t.method2();
}
}
按照希望编译并运行:
test@183f74d
test@183f74d
null
The trick that makes this possible是new Object(){}
的使用,它在现有方法中创建一个新的匿名类,我们试图弄清楚它是否是静态的。这种情况在两种情况下略有不同。
如果目标只是弄清楚方法是否是静态的,我们可以写:
java.lang.reflect.Modifiers.isStatic(new Object(){}.getClass().getEnclosingMethod().getModifiers())
由于我们想要获得this
(如果可用),我们需要做一些稍微不同的事情。幸运的是,对于我们在Java中的对象实例的上下文中定义的类,获得对包含它们的类的隐式引用。 (通常,您使用test.this
语法访问它)。我们需要一种方法来访问test.this
(如果它存在),除非我们实际上无法在任何地方写test.this
因为它在静态情况下在语法上也是无效的。但它确实存在于对象中,作为私有成员变量。这意味着我们可以通过反射找到它,这是getThisOrNull
静态方法对本地匿名类型的作用。
缺点是我们在使用这个技巧的每个方法中都创建了一个匿名类,它可能会增加开销,但是如果你回到角落里寻找一种方法,它至少可以起作用。