超类中的Require';init块引发IllegalArgumentException[Kotlin]
早上好,科特林大师 我有一个继承结构,其中抽象超类实现了一些共享数据检查。编译器不会抱怨,但在执行时JVM会抛出一个IllegalArgumentException 代码超类中的Require';init块引发IllegalArgumentException[Kotlin],kotlin,kotlin-null-safety,Kotlin,Kotlin Null Safety,早上好,科特林大师 我有一个继承结构,其中抽象超类实现了一些共享数据检查。编译器不会抱怨,但在执行时JVM会抛出一个IllegalArgumentException 代码 fun main(args:Array){ val foo=子对象(“非空白”) } 抽象类父类( 打开val名称:String=“非空” ) { 初始化{ require(name.isNotBlank()){“Firstname不能为空”} } } 数据类子类( 覆盖val名称:String=“不为空” ):家长( nam
fun main(args:Array){
val foo=子对象(“非空白”)
}
抽象类父类(
打开val名称:String=“非空”
) {
初始化{
require(name.isNotBlank()){“Firstname不能为空”}
}
}
数据类子类(
覆盖val名称:String=“不为空”
):家长(
name=name
)
例外情况如下所示
线程“main”java.lang.IllegalArgumentException中的异常:指定为非null的参数为null:method kotlin.text.StringsKt\uu StringsJVMKt.isBlank,参数$receiver
在kotlin.text.StringsKt__StringsJVMKt.isBlank处(StringsJVM.kt)
在com.systemkern.Parent.(DataClassInheritance.kt:24)
在com.systemkern.Child.(DataClassInheritance.kt:30)
位于com.systemkern.DataClassInheritanceKt.main(DataClassInheritance.kt:17)
谢谢你的时间
在name.isNotBlank()
上,最好的,您应该会得到如下的lint警告:
访问构造函数中的非最终属性名
在构建父级时,您正在访问名称
属性,但是,名称
在子级中被覆盖。此重写意味着在内部,父类
和子类
都有名称
的私有字段,并且由于创建子类
时首先调用父类
构造函数,子类
的名称
不会被初始化,但是,签入父级将在执行检查时访问子级对属性的重写
这听起来可能很复杂,下面是您示例中反编译字节码的相关部分(简化):
在name.isNotBlank()
上,您应该会收到如下的lint警告:
访问构造函数中的非最终属性名
在构建父级
时,您正在访问名称
属性,但是,名称
在子级
中被覆盖。此重写意味着在内部,父类
和子类
都有名称
的私有字段,并且由于创建子类
时首先调用父类
构造函数,子类
的名称
不会被初始化,但是,签入父级将在执行检查时访问子级对属性的重写
这听起来可能很复杂,下面是您示例中反编译字节码的相关部分(简化):
理解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)
本质上,在构造实际对象之前调用Parent.init,因此引发异常
参考:
[1] 了解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)
本质上,在构造实际对象之前调用Parent.init,因此引发异常
参考:
[1] 非常感谢您抽出时间回答我的问题:)。。。有没有一种方法可以避免这个问题(除了创建一个单独的检查方法)取决于您实际如何使用这些类。如果您需要通过Parent
访问name
属性,但还需要在Child
的构造函数中为数据类功能访问name
,那么您就有点麻烦了。也许您可以将parent
中的属性重命名为其他属性,这样您就不会重写它,而是在每个子类中复制它,并验证parent
中的属性副本。非常感谢您花时间回答我的问题:)。。。有没有一种方法可以避免这个问题(除了创建一个单独的检查方法)取决于您实际如何使用这些类。如果您需要通过Parent
访问name
属性,但还需要在Child
的构造函数中为数据类功能访问name
,那么您就有点麻烦了。也许您可以将parent
中的属性重命名为其他属性,这样您就不会覆盖它,而是在每个子类中复制它,并验证它在parent
中的副本。
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
}
}
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")
}
}
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