Sealed class in 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(密封类)的构造函数。可见性修饰符只是例外吗?
https://kotlinlang.org/docs/reference/visibility-modifiers.html#classes-and-interfaces
对于在类内声明的成员:
private
表示仅在该类内可见(包括其所有成员);
答案 0 :(得分:3)
您可以通过查看生成的字节码来了解正在发生的事情(可以通过转到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 ...
}
您仍然不能从Kotlin继承Expr
,因为Kotlin编译器知道它是文件元数据中的密封类,因此会予以尊重。
对于Java客户端代码,您无法自己访问同一构造函数,因为DefaultConstructorMarker
在其所在的kotlin.jvm.internal
包中是包私有的,因此即使您写出导入手动声明,编译器将不允许。
我的猜测是,只有在编译时才能强制执行包私有的可见性,这就是为什么Kotlin编译器能够输出与上面的代码段相对应的字节码(虽然不确定)。
答案 1 :(得分:3)
考虑以下代码:
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 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")
}
因此,允许密封类的私有构造函数在类之外(但在同一文件中)可以被认为是使代码更简洁的增强功能。