bil*_*.cn 3 java generics lambda java-8
我试图在一些高级包装器中使用Java接口作为mixins D.
interface WrapsD {
D getWrapped();
}
interface FeatureA extends WrapsD {
default ...
}
interface FeatureB extends WrapsD {
default ...
}
abstract class DWrapperFactory<T extends WrapsD> {
protected T doWrap(D d) {
return () -> d; // <- does not work
}
}
interface FeatureAB extends FeatureA, FeatureB {
}
class ProducingDWithFeatureAB extends DWrapperFactory<FeatureAB> {
protected FeatureAB doWrap(D d) {
return () -> d; // <- has to repeat this
}
}
Run Code Online (Sandbox Code Playgroud)
如图所示ProducingDWithFeatureAB,doWrap即使身体相同,也必须在每个子类中实施.(Java泛型真正被打破的另一个例子.)
由于我已经需要创建具体的类,ProducingDWithFeatureAB因为其他原因和JRE中存在的代码来合成lambda类,因此应该可以doWrap使用反射只写一次.我想知道如何做到这一点.
(doWrap过去使用实现接口的匿名内部类来实现,这更加生物背板.)
这与泛型无关; 你的通用例子只是模糊了真正的问题.
下面是问题的核心:lambda表达式需要一个目标类型是一个功能接口,而且目标类型必须静态编译器知道的.您的代码没有提供.例如,以下代码将获得相同的错误,原因相同:
Object o = arg -> expr;
Run Code Online (Sandbox Code Playgroud)
这里,Object不是函数接口,lambda表达式只能在类型为(兼容)功能接口的上下文中使用.
泛型的使用使它更容易混淆(我认为你也在混淆泛型如何工作),但最终这将是最底层的问题.
你必须要了解的第一件事是,这是一种形式的方法
public Function<X,Y> fun() {
return arg -> expr;
}
Run Code Online (Sandbox Code Playgroud)
是相当于:
public Function<X,Y> fun() {
return DeclaringClass::lambda$fun$0;
}
private static Y lambda$fun$0(X arg) {
return expr;
}
Run Code Online (Sandbox Code Playgroud)
而类型X和Y来自目标接口的功能签名.虽然功能接口的实际实例是在运行时生成的,但您需要执行一个由编译器生成的具体化目标方法.
您可以反射地为单个目标方法生成不同接口的实例,但仍然要求所有这些功能接口具有相同的功能签名,例如映射X到Y,这会降低动态解决方案的实用性.
在您的情况下,所有目标接口确实具有相同的功能签名,这是可能的,但我必须强调整个软件设计对我来说是有问题的.
为了实现动态生成,我们必须如上所述去掉lambda表达式,并将捕获的变量d作为附加参数添加到目标方法中.由于您的特定函数没有参数,因此它将捕获d的唯一方法参数:
protected T doWrap(D d) {
Class<T> type=getActualT();
MethodHandles.Lookup l=MethodHandles.lookup();
try
{
MethodType fType = MethodType.methodType(D.class);
MethodType tType = fType.appendParameterTypes(D.class);
return type.cast(LambdaMetafactory.metafactory(l, "getWrapped",
tType.changeReturnType(type), fType,
l.findStatic(DWrapperFactory.class, "lambda$doWrap$0", tType), fType)
.getTarget().invoke(d));
}
catch(RuntimeException|Error t) { throw t; }
catch(Throwable t) { throw new IllegalStateException(t); }
}
private static D lambda$doWrap$0(D d) {
return d;
}
Run Code Online (Sandbox Code Playgroud)
您必须实现getActualT()应该返回正确的类对象的方法,如果实际的子类DWrapperFactory是正确的可再生类型,这是可能的,如您所述.然后,该方法doWrap将动态生成的适当实例T,调用脱糖lambda表达式的方法用的捕获值d-all假设类型T的确是一个功能性的接口,其不能在编译时间证明.
请注意,即使在运行时,LambdaMetafactory也不会检查不变量是否成立,如果T不是正确的功能接口(及其子类WrapsD),您可能会在以后抛出错误.
现在比较重复该方法
protected SubtypeOfWrapsD doWrap(D d) {
return () -> d;
}
Run Code Online (Sandbox Code Playgroud)
在每种必须存在的可再生类型中......
| 归档时间: |
|
| 查看次数: |
570 次 |
| 最近记录: |