我本质上是在问与这个老问题相同的问题,但针对的是 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 中是不可能的,例如 这个解决方案。 Java 14中有更优雅的解决方案吗?
从方法引用中获取方法信息从来都不是 JDK 开发人员的目标,因此没有做出任何努力来改变这种情况。
但是,您的链接中显示的方法可以简化。您可以在序列化时简单地拦截原始
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$1
的内容。当还将下一行更改为 var lambda = (Consumer<String>&Serializable)GetSerializedLambda::main;
时,代码将始终打印合成方法名称,因为使用辅助方法是不可避免的。但对于 javac
,其名称更像是 lambda$main$f23f6912$1
,而不是 Eclipse 的 lambda$1
。
换句话说,您可能会遇到令人惊讶的实现细节。不要依赖此类信息的可用性来编写应用程序。
(伟大的)已接受答案的更新版本,使用 Java 7 try-with-resources、Java 16“instanceof 的模式匹配”并删除了一个多余的
IOException
。
含义:更新至 Java 17 LTS。
(我不允许发表评论)
/**
* Compiler safe way to get a method name from a lambda / anonymous function.
*/
public class GetSerializedLambda extends ObjectOutputStream {
// Test this class.
public static void main(String[] args) {
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, SecurityException {
super(OutputStream.nullOutputStream());
super.enableReplaceObject(true);
}
@Nullable
@Override
protected Object replaceObject(Object obj) {
if (obj instanceof SerializedLambda serializedLambda) {
this.info = serializedLambda;
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);
}
}
}