![](/img/trans.png)
[英]Kotlin - use compiled regex inside init block throws null pointer exception
[英]Kotlin Null Pointer Exception from value not being initialized prior to parent classes constructor/init block running
我在编写 Android 应用程序时正在学习 Kotlin,但由于父构造函数和初始化的执行顺序,我的代码遇到了一个有趣的问题。
我创建了一个更简单的例子来说明我的经历:
class IntHolder(var i: Int)
open class A(var i: Int) {
init {
println("A init block id: ${getId()}") // this line calls getId() triggering the NPE
}
open fun getId(): String {
return "A${i.toString()}"
}
override fun toString(): String {
return "A-(i={$i})"
}
}
class B(i: Int, var h: IntHolder): A(i) {
init {
println("B init block i = ${i}")
println(" `${doSomething()}`")
}
override fun getId(): String {
return "B-${h.i}-${super.getId()}" // NPE on this line
}
override fun toString(): String {
return "B(i=${i})"
}
fun doSomething(): String {
return "something ".repeat(i)
}
}
fun main() {
println("creating object a = A(4)")
val a = A(4)
println("creating object b = B(6)")
val b = B(6, IntHolder(8)) // This is the line in main at the start of the stack trace
println(b.doSomething())
}
或者这里有一个可编辑的副本: https : //pl.kotl.in/17GKHAFRa
这会导致 NullPointerException 当B
的构造函数调用A
的构造函数调用getId()
并且因为这实际上是B
类的对象,即B.getId()
和B
的getId()
引用B
成员但他们没有'尚未初始化为传递给构造函数的值,所以我得到一个NullPointerException
。
实际上,由B
表示的基类是我的,而由A
表示的父类是来自 Android 库的 Java 类。
你会建议如何解决这个问题?
编辑:
我继承的基类是android.opengl.GLSurfaceView
,被调用的函数是getHolder
。 它在 init 中调用,由各种构造函数调用。
我正在关注这个在动态壁纸中使用 GLSurfaceView 的教程,它讨论了覆盖getHolder
来调用[WallpaperService.Engine].getSurfaceHolder()
但没有具体说明我如何将WallpaperSerice.Engine
传递到我从GLSurfaceView
继承的类中所以它的getHolder
可以称之为getSurfaceHolder
这包含在Effective Java Item 19 中:
构造函数不得直接或间接调用可覆盖的方法。 如果违反此规则,将导致程序失败。 超类构造函数在子类构造函数之前运行,因此子类中的覆盖方法将在子类构造函数运行之前被调用。 如果覆盖方法依赖于子类构造函数执行的任何初始化,则该方法将不会按预期运行。 为了具体说明,这里有一个违反此规则的类:
public class Super {
// Broken - constructor invokes an overridable method
public Super() {
overrideMe();
}
public void overrideMe() { }
}
这是一个覆盖 overrideMe 方法的子类,该方法被 Super 的唯一构造函数错误地调用:
public final class Sub extends Super {
// Blank final, set by constructor
private final Instant instant;
Sub() {
instant = Instant.now();
}
// Overriding method invoked by superclass constructor
@Override public void overrideMe() {
System.out.println(instant);
}
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe();
}
}
您可能希望此程序打印出即时两次,但第一次打印出 null,因为 Super 构造函数在 Sub 构造函数有机会初始化即时字段之前调用了 overrideMe。 请注意,该程序在两个不同状态下观察最终场! 另请注意,如果 overrideMe 立即调用了任何方法,则在 Super 构造函数调用 overrideMe 时它会抛出 NullPointerException。 这个程序没有抛出 NullPointerException 的唯一原因是 println 方法容忍空参数。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.