【发布时间】:2016-10-15 05:00:12
【问题描述】:
因为 Java 8 接口可以有默认方法。 我知道如何从实现方法显式调用该方法,即 (见Explicitly calling a default method in Java)
但是我如何显式地调用默认方法,例如在代理上使用反射?
例子:
interface ExampleMixin {
String getText();
default void printInfo(){
System.out.println(getText());
}
}
class Example {
public static void main(String... args) throws Exception {
Object target = new Object();
Map<String, BiFunction<Object, Object[], Object>> behavior = new HashMap<>();
ExampleMixin dynamic =
(ExampleMixin) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{ExampleMixin.class}, (Object proxy, Method method, Object[] arguments) -> {
//custom mixin behavior
if(behavior.containsKey(method.getName())) {
return behavior.get(method.getName()).apply(target, arguments);
//default mixin behavior
} else if (method.isDefault()) {
//this block throws java.lang.IllegalAccessException: no private access for invokespecial
return MethodHandles.lookup()
.in(method.getDeclaringClass())
.unreflectSpecial(method, method.getDeclaringClass())
.bindTo(target)
.invokeWithArguments();
//no mixin behavior
} else if (ExampleMixin.class == method.getDeclaringClass()) {
throw new UnsupportedOperationException(method.getName() + " is not supported");
//base class behavior
} else{
return method.invoke(target, arguments);
}
});
//define behavior for abstract method getText()
behavior.put("getText", (o, a) -> o.toString() + " myText");
System.out.println(dynamic.getClass());
System.out.println(dynamic.toString());
System.out.println(dynamic.getText());
//print info should by default implementation
dynamic.printInfo();
}
}
编辑:我知道在How do I invoke Java 8 default methods refletively 中提出了类似的问题,但这并没有解决我的问题,原因有两个:
- 该问题中描述的问题旨在如何通过反射调用它一般 - 因此默认方法和覆盖方法之间没有区别 - 这很简单,您只需要一个实例。
- 其中一个答案 - 使用方法句柄 - 仅适用于讨厌的 hack (imho),例如将访问修饰符更改为查找类的字段,这是同一类别的“解决方案”,如下所示:Change private static final field using Java reflection:很高兴知道这是可能的,但我不会在生产中使用它 - 我正在寻找一种“官方”的方式来做到这一点。
IllegalAccessException 被抛出unreflectSpecial
Caused by: java.lang.IllegalAccessException: no private access for invokespecial: interface example.ExampleMixin, from example.ExampleMixin/package
at java.lang.invoke.MemberName.makeAccessException(MemberName.java:852)
at java.lang.invoke.MethodHandles$Lookup.checkSpecialCaller(MethodHandles.java:1568)
at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(MethodHandles.java:1227)
at example.Example.lambda$main$0(Example.java:30)
at example.Example$$Lambda$1/1342443276.invoke(Unknown Source)
【问题讨论】:
-
"我正在寻找一种“官方”的方式来做到这一点" 我可能弄错了,但我担心官方你不应该能够调用方法如果您的子类型覆盖了它,则来自超类型。假设您的超类型有
acceptSquare方法可以接受任何正方形,但是您的子类型专门只处理红色正方形,因此它相应地覆盖它以添加颜色测试(之后它调用@987654330 @)。因此,允许某人从此类方法的超类型(甚至通过反射)从外部实现调用可能是一个很大的安全漏洞。 -
mixins 怎么样 - 使用动态代理向现有类添加功能?即我有一个实例,并希望通过在运行时向实例“添加”具有默认方法的接口来添加其他功能。一定有办法
-
我相应地更新了示例,一开始希望它尽可能简单,但希望我的意图现在变得更清晰
-
default方法的调用目标必须是该interface的实例。在您的示例代码中,它是一个Object——它应该如何工作?
标签: java reflection java-8 default-method