![](/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.