Pull to refresh

Comments 15

По декомпилированному коду видно, что полученный синглтон похож на eager реализацию синглтона, он создается в тот момент, когда класслоудер загружает класс.

Ну да, но это не проблема, класс-то все равно загрузится в момент первого обращения, так что можно считать это lazy-реализацией.

По факту да, так и есть. Впрочем, никто и не говорил, что это проблема =)

А, пардон, значит, неправильно Ваш посыл понял. Мне показалось, что Вы сказали это в том ключе, мол, реализация eager, значит это не настоящий синглтон.

Eager вполне себе нормальный синглтон) Вообще как по мне "ненастоящим" синглноном можно назвать только инстанс образованный какой-нибудь DI библиотекой в скоупе Singleton)

Теоретически – да :) На практике же большинство ожидает от них ленивость, потокобезопасность, а то и защиту от рефлексии (хотят тут уже перебор, как по мне).

Несколько моментов:


  • Часто можно смотреть не только Kotlin->ByteCode->Java, а еще и скомпиленный JS. Но, конечно, учитывать, что бэкенды разные.
  • Интересно посмотреть, как сделаны замыкания.
  • Интересно посмотреть, как сделаны всякие около-reflection штуки. И тут Kotlin->ByteCode->Java не поможет. На банальном println(::main) отсыпется
  • Интересно посмотреть во что всякие конструкторы inner превращаются

Ну то есть всё в целом ожидаемо, но интересно.

На банальном println(::main) отсыпется

Уже не должен, по идее. Накидайте пожалуйста примеров, которые вам интересно было бы посмотреть, я их потестирую. Только лучше полными/компилирующимися примерами — так как Котлина не знаю, к сожалению, могу только скормить пример как есть.
Уже не должен, по идее.

Почему "уже"? Почему "не должен"?
Полный код:


fun main(args: Array<String>) {
    println(::main)
}

Код компилируется, работает. Вывод:


fun main(kotlin.Array<kotlin.String>): kotlin.Unit

Байткод показывается нормально.
Если его прогнать Show bytecode и Decompile, то там некомпилируемая шляпа:


   public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      <undefinedtype> var1 = null.INSTANCE;
      System.out.println(var1);
   }

Но это, пожалуй, простительно. Function references завязаны на reflection, а с ним в Котлине с всё непросто: приходится поддерживать и интероп с java, и js/native/ir, и котлиновые фичи (локальные функции, например). Это прослеживается и в трекере и в документации, особенно если посмотреть на JS.

Если его прогнать Show bytecode и Decompile, то там некомпилируемая шляпа
Угу, потому что параметер 'vac' (VERIFY_ANONYMOUS_CLASSES) по умолчанию неактивен. В результате декомпилятор считает класс анонимным, который им на самом деле не является. Если активировать, то результат выглядит так:
public static final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");

      final class NamelessClass_1 extends FunctionReference implements Function1 {
         public static final NamelessClass_1 INSTANCE = new NamelessClass_1();

         public final void invoke(@NotNull String[] p1) {
            Intrinsics.checkParameterIsNotNull(p1, "p1");
            TestPrintlnKt.main(p1);
         }

         public final KDeclarationContainer getOwner() {
            return Reflection.getOrCreateKotlinPackage(TestPrintlnKt.class, "main");
         }

         public final String getName() {
            return "main";
         }

         public final String getSignature() {
            return "main([Ljava/lang/String;)V";
         }

         NamelessClass_1() {
            super(1);
         }
      }

      NamelessClass_1 var1 = NamelessClass_1.INSTANCE;
      System.out.println(var1);
}
во-первых не всегда код будет декомпилирован корректно, во-вторых не любой код может быть декомпилирован

В теории практически любой (правда не всегда потом рекомпилируется, но это немного другой вопрос). В 99% случаев если что-то не (или неверно) декомпилируется, то это баг или недоработка. Если есть примеры подобного, дайте пожалуйста.
Почему декомпилировалось со странным (Function0)null.INSTANCE; — я без понятия, вероятнее всего это баг декомпилятора.
Это не баг, просто часть функционала сейчас по умолчанию отключена (см. пример выше, то же самое). Надо включить параметр 'vac', тогда получится:

public final class Test {
   public final void test() {
      final class NamelessClass_1 extends Lambda implements Function0 {
         public static final NamelessClass_1 INSTANCE = new NamelessClass_1();

         public final void invoke() {
            String var1 = "hello";
            System.out.println(var1);
         }

         NamelessClass_1() {
            super(0);
         }
      }

      Function0 action$iv = (Function0)NamelessClass_1.INSTANCE;
      action$iv.invoke();
      String var2 = "world";
      System.out.println(var2);
   }
}
Спасибо, учту! Как включить vac?
Если в коде, то вот здесь поставить 1.
Если в командной строке, то задать -vac=1
Sign up to leave a comment.

Articles