I was under the impression that Foo::new
is just syntactic sugar for () -> new Foo()
and they should behave identically. However it seems not to be the case. Here's the background:
With Java-8 I use a third party library which has an Optional<Foo> foo
and this offending line:
foo.orElseGet(JCacheTimeZoneCache::new);
JCacheTimeZoneCache
uses in its constructor something from the optional JCache library, which I have not in my class path. With a debugger I verified that foo is not null, so it should actually never instantiate a JCacheTimeZoneCache instance and therefore the missing JCache library should not be an issue. However it does explode with stacktrace complaining about the missing JCache library:
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
First I am surprised by this error, as the code does not instantiate JCacheTimeZoneCache at all. Ok, putting JCache into the class path would fix that. But the author of the library did a very different fix:
foo.orElseGet(() -> new JCacheTimeZoneCache());
Now I'm totally surprised? I have actually two questions:
() -> new JCacheTimeZoneCache()
fix that issue?However, I am completely convinced by the @Wayne Bloss answer that function foo () is generally superior. For example, in this Redux video, the instructor always uses syntax like Which is actually shorter and, IMO, clearer.
Foo (pronounced FOO) is a term used by programmers as a placeholder for a value that can change, depending on conditions or on information passed to the program. Foo and other words like it are formally known as metasyntactic variables.
Thus \let\foo\bar defines \foo to have the value that \bar had at the point of definition. On the other hand, \def\foo {\bar} in effect defines \foo to have the value that \bar has at the point of use. This is a simple process.
Eric Raymond, author of New Hacker Dictionary, observes that in Bill Holman's comic strip of the 1930-50 era, "Smokey Stover," the letters "F-O-O" commonly appeared, unexplained and as a kind of running gag on license plates, in picture frames and on the backs of sandwich board signs.
These 2 might be implemented differently, depending on the java compiler you're using and in what case (I haven't narrowed this down, but it's really an implementation detail any ways).
You can check this by looking at the output of javap -v <enclosing class>
, and looking at the BootstrapMethod table. The compiler might generate this for the method reference case:
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;
Specifically, what's important is the MyClass."<init>":()V
. Which means that the constructor of the class used in a MyClass::new
expression is being looked up directly.
For:
JCacheTimeZoneCache::new
The generated invokedynamic
instruction looks up the constructor in the JCacheTimeZoneCache
class directly, and wraps it in a functional interface (using LambdaMetafactory
).
For:
() -> new JCacheTimeZoneCache()
All of the Java compilers that I've looked at so far generate a synthetic static method in the enclosing class containing the lambda's code, and then that is wrapped in a functional interface by the generated invokedynamic
.
The difference being that for the first, the loading of the JCacheTimeZoneCache
class is required, and for the second only the loading of the enclosing class (which is presumably already loaded) is required. Only when the lambda is actually executed the loading of JCacheTimeZoneCache
is required, because that's when it's first needed.
Since this 'fix' is based on an implementation detail, it's not a very good one. There might be a change in the future which affects how non-capturing lambdas (including constructors) are generated: JDK-8186216 which could break the code again.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With