是否有可能(如何)在Runtime Java上获取方法引用的名称?

时间:2018-11-20 06:47:33

标签: java lambda reflection java-8 method-reference

我最近一直在使用许多方法引用和lambda,并想在运行时知道是否可以打印以筛选lambda的来源(即其名称),仅出于调试原因。我想通过在getName()中调用getClass()使用反射可能是可行的,但是我找不到找到原始源引用名称的方法。

我有一个功能界面,例如:

@FunctionalInterface
public interface FooInterface {
    // function etc etc irrelevant
    public void method();

    public default String getName() {
        // returns the name of the method reference which this is used to define
    }
}

然后可以说我希望测试运行该界面,并将功能界面的源打印到屏幕上。

public static void doStuff(FooInterface f) {
    // prints the lambda name that is used to create f
    System.out.println(f.getName());

    // runs the method itself
    f.method();
}

因此,如果我这样做:

doStuff(Foo::aMethodReference);

它应该在屏幕上打印类似“ aMethodReference”的内容,这样我可以在运行时知道正在运行哪些方法,以什么顺序运行等。

考虑到lambda不是完全对象,我非常怀疑这是否可行,但是,我认为可能有解决方法。此外,eclipse调试工具只是说它是lambda,没有任何其他信息,lambda是否保留这些信息中的任何信息吗?还是全部在运行时丢失了?

干杯。 (如果有任何不同,我正在使用JDK 11)

1 个答案:

答案 0 :(得分:1)

正如您所说的,您仅需要将其用于调试目的,这是一个技巧(即肮脏的hack),可让您做自己想做的事。

首先,您的功能接口必须为Serializable

@FunctionalInterface
public interface FooInterface extends Serializable {

    void method();
}

现在,您可以使用此未记录的,内部实现相关的且极具风险的 代码来打印有关方法参考 targeted 的一些信息。到您的FooInterface功能界面:

@FunctionalInterface
public interface FooInterface extends Serializable {

    void method();

    default String getName() {
        try {
            Method writeReplace = this.getClass().getDeclaredMethod("writeReplace");
            writeReplace.setAccessible(true);
            SerializedLambda sl = (SerializedLambda) writeReplace.invoke(this);
            return sl.getImplClass() + "::" + sl.getImplMethodName();
        } catch (Exception e) {
            return null;
        }
    }
}

调用此方法时:

doStuff(Foo::aMethodReference);

您将看到以下输出:

package/to/the/class/Foo::aMethodReference

注1:我在this article by Peter Lawrey中看到了这种方法。

注意2:我已经使用openjdk version "11" 2018-09-25java version "1.8.0_192"对此进行了测试。