getpath example clase java lambda method-reference

example - getpath() java



¿Cuál es la diferencia entre Foo:: new y()-> new Foo()? (1)

Tenía la impresión de que Foo::new es solo azúcar sintáctica para () -> new Foo() y deberían comportarse de manera idéntica. Sin embargo, parece que no es el caso. Aquí está el fondo:

Con Java-8, uso una biblioteca de terceros que tiene un Optional<Foo> foo y esta línea ofensiva:

foo.orElseGet(JCacheTimeZoneCache::new);

JCacheTimeZoneCache usa en su constructor algo de la biblioteca de JCache opcional, que no tengo en la ruta de mi clase. Con un depurador, verifiqué que foo no es nulo, por lo que nunca debería crear una instancia de JCacheTimeZoneCache y, por lo tanto, la biblioteca de JCache que falta no debería ser un problema. Sin embargo, explota con stacktrace quejándose de la biblioteca JCache que falta:

Caused by: java.lang.BootstrapMethodError: java.lang.IllegalAccessError: no such constructor: net.fortuna.ical4j.util.JCacheTimeZoneCache.<init>()void/newInvokeSpecial at net.fortuna.ical4j.model.TimeZoneLoader.cacheInit(TimeZoneLoader.java:275) ~[ical4j-3.0.0.jar:na] at net.fortuna.ical4j.model.TimeZoneLoader.<init>(TimeZoneLoader.java:81) ~[ical4j-3.0.0.jar:na] at net.fortuna.ical4j.model.TimeZoneRegistryImpl.<init>(TimeZoneRegistryImpl.java:125) ~[ical4j-3.0.0.jar:na] at net.fortuna.ical4j.model.TimeZoneRegistryImpl.<init>(TimeZoneRegistryImpl.java:116) ~[ical4j-3.0.0.jar:na] at net.fortuna.ical4j.model.DefaultTimeZoneRegistryFactory.createRegistry(DefaultTimeZoneRegistryFactory.java:48) ~[ical4j-3.0.0.jar:na] at net.fortuna.ical4j.data.CalendarBuilder.<init>(CalendarBuilder.java:105) ~[ical4j-3.0.0.jar:na] at de.malkusch.trashcollection.infrastructure.schedule.ical.VEventRepository.downloadVEvents(VEventRepository.java:46) ~[classes/:na] at de.malkusch.trashcollection.infrastructure.schedule.ical.VEventRepository.<init>(VEventRepository.java:35) ~[classes/:na] at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:1.8.0_172] at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) ~[na:1.8.0_172] at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:1.8.0_172] at java.lang.reflect.Constructor.newInstance(Constructor.java:423) ~[na:1.8.0_172] at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:170) ~[spring-beans-5.0.7.RELEASE.jar:5.0.7.RELEASE] ... 80 common frames omitted Caused by: java.lang.IllegalAccessError: no such constructor: net.fortuna.ical4j.util.JCacheTimeZoneCache.<init>()void/newInvokeSpecial at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:483) ~[na:1.8.0_172] ... 93 common frames omitted Caused by: java.lang.NoClassDefFoundError: javax/cache/configuration/Configuration at java.lang.invoke.MethodHandleNatives.resolve(Native Method) ~[na:1.8.0_172] at java.lang.invoke.MemberName$Factory.resolve(MemberName.java:975) ~[na:1.8.0_172] at java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:1000) ~[na:1.8.0_172] at java.lang.invoke.MethodHandles$Lookup.resolveOrFail(MethodHandles.java:1394) ~[na:1.8.0_172] at java.lang.invoke.MethodHandles$Lookup.linkMethodHandleConstant(MethodHandles.java:1750) ~[na:1.8.0_172] at java.lang.invoke.MethodHandleNatives.linkMethodHandleConstant(MethodHandleNatives.java:477) ~[na:1.8.0_172] ... 93 common frames omitted Caused by: java.lang.ClassNotFoundException: javax.cache.configuration.Configuration at java.net.URLClassLoader.findClass(URLClassLoader.java:381) ~[na:1.8.0_172] at java.lang.ClassLoader.loadClass(ClassLoader.java:424) ~[na:1.8.0_172] at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:349) ~[na:1.8.0_172] at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ~[na:1.8.0_172] ... 99 common frames omitted

Primero, me sorprende este error, ya que el código no crea una instancia de JCacheTimeZoneCache. Ok, poner a JCache en la ruta de clase lo arreglaría. Pero el autor de la biblioteca hizo una solución muy diferente:

foo.orElseGet(() -> new JCacheTimeZoneCache());

Ahora estoy totalmente sorprendido? En realidad tengo dos preguntas:

  1. ¿Por qué JCacheTimeZoneCache :: new causó esa excepción en primer lugar, cuando el constructor nunca fue llamado?
  2. ¿Por qué () -> new JCacheTimeZoneCache() solucionó ese problema?

Estos 2 podrían implementarse de manera diferente, dependiendo del compilador de java que estés usando y en qué caso (no lo he reducido, pero en realidad es un detalle de implementación).

Puede verificar esto mirando la salida de javap -v <enclosing class> , y mirando la tabla BootstrapMethod. El compilador podría generar esto para el caso de referencia del método:

1: #22 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; Method arguments: #23 ()Ljava/lang/Object; #27 REF_newInvokeSpecial MyClass."<init>":()V #25 ()LMyClass;

Específicamente, lo que es importante es MyClass."<init>":()V Lo que significa que el constructor de la clase utilizada en una expresión MyClass::new se está buscando directamente.

Por:

JCacheTimeZoneCache::new

La instrucción invokedynamic generada busca el constructor en la clase JCacheTimeZoneCache directamente y lo envuelve en una interfaz funcional (usando LambdaMetafactory ).

Por:

() -> new JCacheTimeZoneCache()

Todos los compiladores de Java que he visto hasta ahora generan un método estático sintético en la clase adjunta que contiene el código de lambda, y luego se envuelve en una interfaz funcional por el invokedynamic generado.

La diferencia es que para la primera, se requiere la carga de la clase JCacheTimeZoneCache , y para la segunda, solo se requiere la carga de la clase JCacheTimeZoneCache (que probablemente ya está cargada). Solo cuando la lambda se ejecuta realmente se requiere la carga de JCacheTimeZoneCache , porque es cuando primero se necesita.

Dado que este ''arreglo'' se basa en un detalle de implementación, no es muy bueno. Podría haber un cambio en el futuro que afecte la forma en que se generan las lambdas no capturadoras (incluidos los constructores): JDK-8186216 que podría volver a romper el código.