如何获取Java14方法引用的MethodInfo?

How to get the MethodInfo of a Java 14 method reference?

我基本上问的和 this old question 一样,但是 Java 14 而不是 Java 8。为了避免回答者导航到旧问题的麻烦,我'我会在这里改写它。 我想从引用的方法中获取函数的名称。以下 Java 代码应该可以让您了解:

public class Main
{
    public static void main(String[] args)
    {
       printMethodName(Main::main);
    }

    private static void printMethodName(Consumer<String[]> theFunc)
    {
        String funcName = // somehow get name from theFunc
        System.out.println(funcName)
    }
 }

C# 中的等价物是:

public class Main
{
    public static void Main()
    {
        var method = Main.Main;
        PrintMethodName(method)
    }

    private static void PrintMethodName(Action action)
    {
        Console.WriteLine(action.GetMethodInfo().Name);
    }
}

根据旧问题的公认答案,如果没有大量工作,这在 Java 8 中是不可能的,例如 this solution。 Java14有没有更优雅的方案?

从方法引用中获取方法信息从来都不是 JDK 开发人员的目标,因此没有努力改变这种情况。

但是,您 link 中显示的方法可以简化。无需序列化信息、修补序列化数据并使用替换对象恢复信息,您可以在序列化时简单地拦截原始 SerializedLambda 对象。

例如

public class GetSerializedLambda extends ObjectOutputStream {
    public static void main(String[] args) { // example case
        var lambda = (Consumer<String[]>&Serializable)GetSerializedLambda::main;
        SerializedLambda sl = GetSerializedLambda.get(lambda);
        System.out.println(sl.getImplClass() + " " + sl.getImplMethodName());
    }

    private SerializedLambda info;

    GetSerializedLambda() throws IOException {
      super(OutputStream.nullOutputStream());
      super.enableReplaceObject(true);
    }

    @Override protected Object replaceObject(Object obj) throws IOException {
      if(obj instanceof SerializedLambda) {
        info = (SerializedLambda)obj;
        obj = null;
      }
      return obj;
    }

    public static SerializedLambda get(Object obj) {
        try {
            GetSerializedLambda getter = new GetSerializedLambda();
            getter.writeObject(obj);
            return getter.info;
        } catch(IOException ex) {
            throw new IllegalArgumentException("not a serializable lambda", ex);
        }
    }
}

将打印 GetSerializedLambda main。此处使用的唯一较新功能是 OutputStream.nullOutputStream() 以立即删除书面信息。在 JDK 11 之前,您可以写入 ByteArrayOutputStream 并在操作后删除信息,这只是效率稍低。例子中同样使用了var,但这与实际获取方法信息的操作无关

限制与JDK 8相同。它需要一个可序列化的方法引用。此外,不保证实现将直接映射到方法。例如,如果您将示例的声明更改为 public static void main(String... args),则在使用 Eclipse 编译时它将打印类似 lambda 的内容。当还将下一行更改为 var lambda = (Consumer<String>&Serializable)GetSerializedLambda::main; 时,代码将始终打印合成方法名称,因为使用辅助方法是不可避免的。但是在 javac 的情况下,名称更像是 lambda$main$f23f6912 而不是 Eclipse 的 lambda.

换句话说,您可能会遇到令人惊讶的实现细节。不要依赖此类信息的可用性来编写应用程序。