我有一个采用功能参数的方法,例如a Runnable.由于它是一个库方法,我希望它使用从函数参数派生的记录器.调用getClass函数参数适用于普通类,我可以getEnclosingClass用于嵌套或匿名类; 但如果它是一个lambda表达式,它会返回一个含有的模糊名称$$Lambda$,我可以像这样手动剥离:
Class<?> type = runnable.getClass();
String canonical = type.getCanonicalName();
int lambdaOffset = canonical.indexOf("$$Lambda$");
if (lambdaOffset > 0) {
try {
type = Class.forName(canonical.substring(0, lambdaOffset));
} catch (ClassNotFoundException e) {
// strange, but we can stick to the type we already have
}
}
Run Code Online (Sandbox Code Playgroud)
正如你所看到的那样,这不是很优雅,也可能不便携.我试过getEnclosingClass,getEnclosingMethod和getEnclosingConstructor,但他们都返回null.
有任何想法吗?
正如Tassos Bassoukos已经提到的,它是设计的.
Lambda(类)的字节码是在运行时生成的.所以你得到的是班级的实际名称.并且名称生成为target class name + "$$Lambda$" + a counter.
找一个小片段进行演示.
package sub.optimal;
import static java.lang.System.out;
public class EnclosingClass {
static class InnerRunnable implements Runnable {
@Override
public void run() {
out.println("--- inner class");
}
}
public static void main(String... args) {
showIdentity(() -> System.out.println("--- lambda 1"));
showIdentity(() -> System.out.println("--- lambda 2"));
showIdentity(new InnerRunnable());
showIdentity(new Runnable() {
@Override
public void run() {
out.println("--- anonymous class");
}
});
}
private static void showIdentity(Runnable runnable) {
runnable.run();
Class<? extends Runnable> clazz = runnable.getClass();
out.printf("class name : %s%n", clazz.getName());
out.printf("class hashcode : %s%n", clazz.hashCode());
out.printf("canonical name : %s%n", clazz.getCanonicalName());
out.printf("enclosing class: %s%n", clazz.getEnclosingClass());
out.println();
}
}
Run Code Online (Sandbox Code Playgroud)
产量
--- lambda 1
class name : sub.optimal.EnclosingClass$$Lambda$1/2147972
class hashcode : 2147972
canonical name : sub.optimal.EnclosingClass$$Lambda$1/2147972
enclosing class: null
--- lambda 2
class name : sub.optimal.EnclosingClass$$Lambda$2/10376386
class hashcode : 10376386
canonical name : sub.optimal.EnclosingClass$$Lambda$2/10376386
enclosing class: null
--- inner class
class name : sub.optimal.EnclosingClass$InnerRunnable
class hashcode : 28014437
canonical name : sub.optimal.EnclosingClass.InnerRunnable
enclosing class: class sub.optimal.EnclosingClass
--- anonymous class
class name : sub.optimal.EnclosingClass$1
class hashcode : 19451386
canonical name : null
enclosing class: class sub.optimal.EnclosingClass
Run Code Online (Sandbox Code Playgroud)
我找到了benjiweber的一个很酷的解决方案。归结为将 lamda 序列化为 a java.lang.invoke.SerializedLambda,然后获取其声明类:
private static final int COUNT = 1_000_000;
private static boolean first = true;
public static void main(String[] args) {
long t = System.currentTimeMillis();
for (int i = 0; i < COUNT; i++) {
showIdentity(() -> {
});
}
String time = NumberFormat.getNumberInstance().format((double) (System.currentTimeMillis() - t) / COUNT);
System.out.println("time per call: " + time + "ms");
}
public interface MethodAwareRunnable extends Runnable, Serializable {}
private static void showIdentity(MethodAwareRunnable consumer) {
consumer.run();
String name = name(consumer);
if (first) {
first = false;
Class<?> clazz = consumer.getClass();
System.out.printf("class name : %s%n", clazz.getName());
System.out.printf("class hashcode : %s%n", clazz.hashCode());
System.out.printf("canonical name : %s%n", clazz.getCanonicalName());
System.out.printf("enclosing class: %s%n", clazz.getEnclosingClass());
System.out.printf("lambda name : %s%n", name);
}
}
private static String name(Object consumer) {
return method(consumer).getDeclaringClass().getName();
}
private static SerializedLambda serialized(Object lambda) {
try {
Method writeMethod = lambda.getClass().getDeclaredMethod("writeReplace");
writeMethod.setAccessible(true);
return (SerializedLambda) writeMethod.invoke(lambda);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Class<?> getContainingClass(SerializedLambda lambda) {
try {
String className = lambda.getImplClass().replaceAll("/", ".");
return Class.forName(className);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Method method(Object lambda) {
SerializedLambda serialized = serialized(lambda);
Class<?> containingClass = getContainingClass(serialized);
return Arrays.stream(containingClass.getDeclaredMethods())
.filter(method -> Objects.equals(method.getName(), serialized.getImplMethodName()))
.findFirst()
.orElseThrow(RuntimeException::new);
}
Run Code Online (Sandbox Code Playgroud)
这是很多代码,但在我的机器上,开销约为 0.003 毫秒,这对于大多数用例来说是可以的。
您还可以做其他很酷的事情,例如:
Map<String, String> hash = hash(
hello -> "world",
bob -> "was here"
);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2661 次 |
| 最近记录: |