在超类中需要'init块引发IllegalArgumentException

早安Kotlin大师。

我有一个继承结构,其中抽象的超类实现了一些共享的数据检查。 编译器不会抱怨,但在执行时,JVM将引发IllegalArgumentException

代码

fun main(args: Array<String>) { val foo = Child("NOT_BLANK") } abstract class Parent( open val name: String = "NOT_BLANK" ) { init { require(name.isNotBlank()) { "Firstname must not be blank" } } } data class Child( override val name: String = "NOT_BLANK" ) : Parent( name = name ) 

异常看起来如下

 Exception in thread "main" java.lang.IllegalArgumentException: Parameter specified as non-null is null: method kotlin.text.StringsKt__StringsJVMKt.isBlank, parameter $receiver at kotlin.text.StringsKt__StringsJVMKt.isBlank(StringsJVM.kt) at com.systemkern.Parent.<init>(DataClassInheritance.kt:24) at com.systemkern.Child.<init>(DataClassInheritance.kt:30) at com.systemkern.DataClassInheritanceKt.main(DataClassInheritance.kt:17) 

谢谢你的时间

祝一切顺利

name.isNotBlank() ,你应该得到像这样的皮棉警告:

在构造函数中访问非final属性名称

您正在构建Parent时访问name属性,但是,在Child覆盖name 。 这个覆盖意味着在内部, Parent类和Child类都有专用的name字段,而且由于Parent构造函数是创建Child对象时调用的第一个对象,因此Childname不会被初始化, Parent在进行支票时将访问Child对财产的覆盖。

这听起来可能听起来很复杂,下面是你的示例(简化)的反编译字节码的相关部分:

 public abstract class Parent { @NotNull private final String name; @NotNull public String getName() { return this.name; } public Parent(@NotNull String name) { super(); this.name = name; if(!StringsKt.isBlank(this.getName())) { // uses getName, which is overridden in // Child, so Child's field is returned throw new IllegalArgumentException("Firstname must not be blank"); } } } public final class Child extends Parent { @NotNull private final String name; @NotNull @Override public String getName() { return this.name; } public Child(@NotNull String name) { super(name); // calls super constructor this.name = name; // sets own name field } } 

一些代码来理解kotlin初始化器的执行顺序[1]

  open class Parent { private val a = println("Parent.a") constructor(arg: Unit=println("Parent primary constructor default argument")) { println("Parent primary constructor") } init { println("Parent.init") } private val b = println("Parent.b") } class Child : Parent { val a = println("Child.a") init { println("Child.init 1") } constructor(arg: Unit=println("Child primary constructor default argument")) : super() { println("Child primary constructor") } val b = println("Child.b") constructor(arg: Int, arg2:Unit= println("Child secondary constructor default argument")): this() { println("Child secondary constructor") } init { println("Child.init 2") } } 

儿童输出(1)

 Child secondary constructor default argument Child primary constructor default argument Parent primary constructor default argument Parent.a Parent.init Parent.b Parent primary constructor Child.a Child.init 1 Child.b Child.init 2 Child primary constructor Child secondary constructor 

本质上,在构造实际对象之前调用Parent.init,并抛出异常。

参考:[1] https://medium.com/keepsafe-engineering/an-in-depth-look-at-kotlins-initializers-a0420fcbf546