在静态方法中表现不同的表达式
我正在尝试编写一个Java源代码的表达式或一系列语句,当在static
方法中写入时,求值为null
,但如果该方法是非静态的,则求值this
。
我最初的想法是静态与非静态’重载’,如下所示:
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
修饰符的存在?
这可以通过Java的reflection工具的技巧和一些帮助来实现。 这很难看,但它有效:
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
使这成为可能的技巧是使用new Object(){}
,它在现有方法中创建一个新的匿名类,我们试图弄清楚它是否是静态的。 这种情况在两种情况之间略有不同。
如果目标只是弄清楚方法是否是静态的,我们可以写:
java.lang.reflect.Modifiers.isStatic(new Object(){}.getClass().getEnclosingMethod().getModifiers())
由于我们希望得到this
(如果可用),我们需要做一些稍微不同的事情。 幸运的是,对于我们在Java中的对象的实例的上下文中定义的类,获得对包含它们的类的隐式引用。 (通常你可以使用test.this
语法访问它)。 我们需要一种方法来访问test.this
如果它存在,除了我们实际上test.this
在任何地方编写test.this
,因为它在静态情况下在语法上也是无效的。 但它确实存在于对象中,作为私有成员变量。 这意味着我们可以使用reflection找到它,这是getThisOrNull
静态方法对本地匿名类型的作用。
缺点是我们在使用这个技巧的每个方法中都创建了一个匿名类,它可能会增加开销,但如果你被置于一个角落并寻找一种方法,它至少可以起作用。