gpt4 book ai didi

kotlin - 为什么非空变量可以有空值?

转载 作者:行者123 更新时间:2023-12-02 01:24:05 24 4
gpt4 key购买 nike

这是我的 kotlin 类:

class Test{
val x: String = run {
y
}

val y: String = run {
x
}
}

尽管变量 x 和 y 被声明为不可为 null 的字符串,但它们最终都为 null。

您可以运行它 here 。正如您所看到的,您最终会因尝试调用 x 或 y 上的方法而出现空指针异常。

为什么这可能?考虑到这一点,你怎么能真正实现零安全呢?

最佳答案

这就是你的类在 Java 中反编译的结果:

public final class Test {
@NotNull
private final String x;
@NotNull
private final String y;

@NotNull
public final String getX() {
return this.x;
}

@NotNull
public final String getY() {
return this.y;
}

public Test() {
Test $this$run = (Test)this;
int var3 = false;
String var5 = $this$run.y;
this.x = var5;
$this$run = (Test)this;
var3 = false;
var5 = $this$run.x;
this.y = var5;
}
}

因此,您的支持字段 xy 首先被声明。 尚未为它们分配值,因此,在 Java 中,这意味着它们的值为 null,因为这是未分配对象引用的默认值。

在 getter 之后,您将看到构造函数,这是进行赋值的地方。有一些奇怪的变量存在,但是

Test $this$run = (Test)this;

基本上是创建一个引用当前对象 this 的变量。我们可以将赋值代码减少到

this.x = this.y // y is null, so x is set to null
this.y = this.x // x is null, so y is set to null

因为对象引用的默认值为null,所以无论您的哪个赋值首先运行,都将始终从另一个变量读取空值(请记住,您没有显式地给 Yet 赋值了)。


基本上,Kotlin 中的初始化顺序很重要,您不能引用尚未声明的内容。像这样也行不通:

class Thing {
val a = b
val b = "hi"
}

在分配 a 的行上,b 的值当前未定义。它也不会在 JVM 上运行,因为该代码反编译后基本上是这样的:

public final class Thing {
@NotNull
private final String a;
@NotNull
private final String b;

public Thing() {
this.a = this.b;
this.b = "hi";
}
}

并且 this.a = this.b 行将失败,因为“b 可能尚未初始化”。您可以在代码的反编译版本中使用相同的技巧来解决这个问题,并将另一个变量分配给 this:

public Thing() {
Thing thing = (Thing) this;
this.a = thing.b;
this.b = "hi";
}

它将运行,但a最终被分配默认值null


所以基本上,您使用的代码是一种绕过这种错误并最终给您带来意想不到的行为的棘手方法。显然你的例子是不现实的(a = b = a的结果本质上是未定义的),但这种情况也可能发生在这种代码中,其中初始化通过其他函数:

class Wow {
val a = doSomething()
val b = 1

fun doSomething() = b
}

在这种情况下,a 在 JVM 上最终为 0,这是 int 的默认值,因为在分配 a< 时 它基本上绕道通过一个函数,在被赋值之前读取 b 。 Kotlin(目前)似乎无法检查此类事物的有效性 - 因此有时尝试通过函数初始化事物时会遇到问题:

class Wow {
// unassigned var
var a: Int
val b = 1

init {
// calls a function that assigns a value to a
doSomething()
}

fun doSomething() { a = 5 }
}

这将失败,因为它无法确定 a 已初始化,即使 init block 这样做了,因为它是作为另一个函数的副作用而发生的。 (并且您可以将该分配埋入任意数量的链式调用中,这可能就是为什么它不是“固定”的事情 - 如果您开始对此类事情做出保证,它需要保持一致!)


所以基本上,在初始化期间,您可以执行编译器无法捕获的操作,这就是您可以绕过非空保证等问题的方法。它不经常出现,但需要注意!我只熟悉 JVM 方面,我假设未定义的行为是特定于平台的。

关于kotlin - 为什么非空变量可以有空值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75338998/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com