簡體   English   中英

在父類構造函數/初始化塊運行之前未初始化值的 Kotlin 空指針異常

[英]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()BgetId()引用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM