使用LambdaMetafactory的AbstractMethodException

时间:2014-12-11 15:30:07

标签: java lambda java-8

作为与Holgers' solution密切相关的后续问题,为什么取消注释覆盖会破坏下面的工作代码?

public static interface StringFunction<N extends Number> extends Function<String, N> {

  // @Override
  // N apply(String t);

}

仅当上述评论未被删除时,此方法才有效:

public static <N extends Number> StringFunction<N> create(Class<N> type) throws Throwable {
  MethodType methodType = MethodType.methodType(type, String.class);
  MethodHandles.Lookup lookup = MethodHandles.lookup();
  MethodHandle handle = lookup.findConstructor(type, 
    MethodType.methodType(void.class, String.class));
  StringFunction<N> f = (StringFunction<N>) LambdaMetafactory.metafactory(lookup, "apply", 
        MethodType.methodType(StringFunction.class), 
        methodType.generic(), handle, methodType).getTarget().invokeExact();
  return f;
}

public static void main(String[] args) throws Throwable {
  System.out.println(create(Byte.class).apply("1"));
  System.out.println(create(Short.class).apply("2"));
  System.out.println(create(Integer.class).apply("3"));
  System.out.println(create(Long.class).apply("4"));
}

运行时抱怨:

Exception in thread "main" java.lang.AbstractMethodError:
Method LambdaFun$$Lambda$1.apply(Ljava/lang/String;)Ljava/lang/Number; is abstract
  at LambdaFun$$Lambda$1/856419764.apply(Unknown Source)
  at LambdaFun.main(LambdaFun.java:28)

1 个答案:

答案 0 :(得分:4)

在使用带有metafactory的通用interface时,您需要了解Generics如何在字节码级别上工作。

声明{/ 1}}方法时

interface

原始类型public static interface StringFunction<N extends Number> extends Function<String, N> { @Override N apply(String t); } 将有一个必须实现StringFunction签名的方法。它还包含一个编译器生成的桥接方法,它覆盖了将委托给Number apply(String)方法的继承方法Object apply(Object)(这是一件好事,例如当abstract在Java下编译时7或更早,我们必须使用altMetafactory明确声明所有必需的桥接方法,比较«this answer»)。

因此您必须将工厂方法更改为:

interface

让它发挥作用。请注意,我们现在保留现在已修复的public static <N extends Number> StringFunction<N> create(Class<N> type) throws Throwable { MethodType methodType = MethodType.methodType(type, String.class); MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle handle = lookup.findConstructor(type, MethodType.methodType(void.class, String.class)); StringFunction<N> f = (StringFunction<N>) LambdaMetafactory.metafactory(lookup, "apply", MethodType.methodType(StringFunction.class), methodType.changeReturnType(Number.class), handle, methodType).getTarget().invokeExact(); return f; } 参数类型,并仅使用String将返回类型更改为其下限。


此外,请注意此代码如何隐藏有关泛型的错误。您在应该由参数methodType.changeReturnType(Number.class)替换的位置使用Integer.class,但它不会立即中断,因为您的示例代码从不尝试分配此类函数返回的值,例如type到该类型的变量,因此您没有注意到StringFunction<Short>返回StringFunction<Short>。我在我的示例代码中修复了这个问题,例如Integer确实返回StringFunction<Short>