繁体   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