values enum all java java-8 type-inference

values - Enum, interfaces y(Java 8) lambdas: el código se compila pero falla en el tiempo de ejecución; es esto esperado?



java enum to list (1)

JDK es Oracle ''JDK 1.8u65 pero el problema se ha visto con "tan bajo como" 1.8u25 también.

Aquí está el SSCCE completo:

public final class Foo { private interface X { default void x() { } } private enum E1 implements X { INSTANCE, ; } private enum E2 implements X { INSTANCE, ; } public static void main(final String... args) { Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(X::x); } }

Este código compila; pero falla en tiempo de ejecución:

Exception in thread "main" java.lang.BootstrapMethodError: call site initialization exception at java.lang.invoke.CallSite.makeSite(CallSite.java:341) at java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:307) at java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:297) at com.github.fge.grappa.debugger.main.Foo.main(Foo.java:38) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) Caused by: java.lang.invoke.LambdaConversionException: Invalid receiver type class java.lang.Enum; not a subtype of implementation type interface com.github.fge.grappa.debugger.main.Foo$X at java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:233) at java.lang.invoke.LambdaMetafactory.metafactory(LambdaMetafactory.java:303) at java.lang.invoke.CallSite.makeSite(CallSite.java:302) ... 8 more

Repararlo en el código es "fácil"; En el método principal, solo tienes que:

// Note the <X> Stream.<X>of(E1.INSTANCE, E2.INSTANCE).forEach(X::x);

EDITAR De hecho, hay una segunda forma, como se menciona en la respuesta aceptada ... Reemplace la referencia del método con un lambda:

Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(x -> x.x());

Entonces, uh. ¿Qué pasa aquí? ¿Por qué el código inicial se compila en primer lugar? Habría esperado que el compilador notara que la referencia del método no estaba en nada Enum<?> Sino en X , pero no ...

¿Qué me estoy perdiendo? ¿Es este un error en el compilador? ¿Un malentendido mío?


Parece que has golpeado JDK-8141508 , que de hecho es un error de javac cuando se trata de tipos de intersección y referencias de métodos. Está programado para ser arreglado en Java 9.

Citando un correo de Remi Forax :

javac tiene problemas con el tipo de intersección que es el tipo de destino de un lambda y la referencia del método. Normalmente, cuando hay un tipo de intersección, javac lo sustituye por el primer tipo del tipo de intersección y agrega conversión cuando sea necesario.

Supongamos que tenemos este código,

public class Intersection { interface I { } interface J { void foo(); } static <T extends I & J> void bar(T t) { Runnable r = t::foo; } public static void main(String[] args) { class A implements I, J { public void foo() {} } bar(new A()); } }

Actualmente, javac genera una referencia de método en J :: foo con una dinámica invocada que toma una I como parámetro, por lo que falla en el tiempo de ejecución. javac debería de-sugar t :: foo en un lambda que tome una I y luego agregue un molde a J como para una llamada a un método de tipo de intersección.

Así que la solución es usar un lambda en su lugar,

Runnable r = t -> t.foo();

Ya he visto este error en alguna parte, pero no pude encontrar un informe de error correspondiente en la base de datos :(

En su código, el Stream creado por Stream.of(E1.INSTANCE, E2.INSTANCE) es del tipo Stream<Enum<?>&Foo.X> , que combina todos los elementos del error: tipos de intersección y referencias de métodos.

Como señaló Remi Forax, una solución alternativa sería:

Stream.of(E1.INSTANCE, E2.INSTANCE).forEach(x -> x.x());

es decir, utilizando una expresión lambda explícita en lugar de una referencia de método.