Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the difference between Foo::new and () -> new Foo()?

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:

  1. Why did JCacheTimeZoneCache::new cause that exception in the first place, when the constructor was never called?
  2. Why did () -> new JCacheTimeZoneCache() fix that issue?
like image 260
Markus Malkusch Avatar asked Jun 30 '18 08:06

Markus Malkusch


People also ask

Which is better function foo () or foo ()?

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.

What is a Foo in C++?

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.

What is the difference between \LET\ foo\bar and \Def\foo√bar?

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.

What is the origin of the F-O-O?

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.


1 Answers

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.

like image 114
Jorn Vernee Avatar answered Oct 23 '22 21:10

Jorn Vernee