Эти 2 могут быть реализованы по-разному, в зависимости от используемого вами Java-компилятора и в каком случае (я не сужал это, но это действительно детали реализации в любом случае).
Вы можете проверить это, посмотрев на вывод javap -v <enclosing class>
и просмотрев таблицу BootstrapMethod. Компилятор может сгенерировать это для справочного случая метода:
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;
В частности, важно MyClass."<init>":()V
. Это означает, что конструктор класса, используемого в выражении MyClass::new
, ищется напрямую.
Для:
JCacheTimeZoneCache::new
Сгенерированная инструкция invokedynamic
непосредственно ищет конструктор в классе JCacheTimeZoneCache
и упаковывает его в функциональный интерфейс (используя LambdaMetafactory
).
Для:
() -> new JCacheTimeZoneCache()
Все компиляторы Java, которые я рассматривал до сих пор, генерируют синтетический статический метод во включающем классе, содержащем лямбда-код, который затем оборачивается в функциональный интерфейс сгенерированным invokedynamic
.
Разница заключается в том, что для первого требуется загрузка класса JCacheTimeZoneCache
, а для второго - только загрузка окружающего класса (который предположительно уже загружен). Только когда действительно выполняется лямбда, требуется загрузка JCacheTimeZoneCache
, потому что именно тогда она и нужна.
Поскольку это «исправление» основано на деталях реализации, оно не очень хорошее. Возможно, в будущем произойдут изменения, которые повлияют на то, как генерируются неперехватывающие лямбды (включая конструкторы): JDK-8186216 , что может снова нарушить код.