Почему закрытый конструктор закрытого класса можно вызывать в подклассе? - PullRequest
0 голосов
/ 10 февраля 2019

Запечатанный класс в Kotlin может иметь только конструктор private.Это означает, что мы можем вызывать конструктор только сам по себе:

Запечатанным классам не разрешено иметь не приватные конструкторы (по умолчанию их конструкторы являются закрытыми).

// `private` and `constructor()` are redundant.
sealed class Expr private constructor()

Но, когда мы используем запечатанный класс, подкласс должен наследовать класс запечатанного:

// Above Kotlin 1.1
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()

Как вы можете видеть код выше, конструктор private запечатанного класса вызывается вне самого запечатанного класса.Когда создается экземпляр подкласса, конструктор parent (sealed class) будет вызван до вызова собственного конструктора подкласса.Это просто исключение для модификаторов видимости?

https://kotlinlang.org/docs/reference/visibility-modifiers.html#classes-and-interfaces

Для членов, объявленных внутри класса: private означает видимый только внутри этого класса (включая все его члены);

Ответы [ 2 ]

0 голосов
/ 10 февраля 2019

Рассмотрим следующий код:

open class A private constructor(var name: String){
    class B : A("B")
    class C : A("C")
}

Приведенный выше код прекрасно компилируется, так как конструктор вызывается внутри класса A. Если класс D пытается наследовать вне A, он не компилируется.

class D : A("D") // Error: Cannot access '<init>': it is private in 'A'

Как упоминалось на странице Запечатанный класс в Kotlin ,

Запечатанный класс может иметь подклассы, но все они должны быть объявлены в одном и том жефайл как сам запечатанный класс.(До Kotlin 1.1 правила были еще более строгими: классы должны были быть вложены в объявление закрытого класса).

Кажется, что kotlin ослабил требование только для вложенных классов.

Итак, следующий код прекрасно работает в 1.1+, но в более ранних версиях он не работает:

sealed class A(var name: String)
class B : A("B")
class C : A("C")

, тогда как в версиях до 1.1 требовался следующий код, который учитывает приватный конструктор.

sealed class A (var name: String){
    class B : A("B")
    class C : A("C")
}

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

0 голосов
/ 10 февраля 2019

Вы можете выяснить, что происходит, взглянув на сгенерированный байт-код (вы можете сделать это, перейдя к Tools -> Kotlin -> Show Kotlin Bytecode и затем выбрав Decompile в появившейся панели.).Декомпиляция в Java показывает этот код для класса Expr:

public abstract class Expr {
   private Expr() {
   }

   // $FF: synthetic method
   public Expr(DefaultConstructorMarker $constructor_marker) {
      this();
   }
}

Так что - это не приватный конструктор для сгенерированного класса Expr со специальным параметром.Затем, как и следовало ожидать, если вы посмотрите, например, на декомпилированный байт-код Const, вы увидите, что он вызывает этот конструктор:

public final class Const extends Expr {
   public Const(double number) {
      super((DefaultConstructorMarker)null);
      this.number = number;
   }

   // other fields and methods ...
}

Вы все еще не можете создать подкласс Expr от Kotlin, потому что компилятор Kotlin знает, что это запечатанный класс из метаданных в файле, и будет учитывать это.

Что касается клиентского кода Java, там вы не можете получить доступ к этому же конструктору самостоятельно, потому чтоDefaultConstructorMarker является частным пакетом в пакете kotlin.jvm.internal, в котором он находится, поэтому даже если вы напишите оператор импорта для него вручную, компилятор не допустит его.

Я предполагаю, что пакет-private видимость может быть применена только во время компиляции, и поэтому компилятор Kotlin может выводить байт-код, соответствующий приведенному выше фрагменту (хотя и не совсем уверен).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...