首页 > 解决方案 > java中可以为null的返回值是否可以放入kotlin中声明为非null的变量中

问题描述

作为一名 Kotlin 的新开发人员,我第一次遇到一个我很乐意得到解释的情况。ViewfindViewById方法可以返回 null (这是我作为 java 开发人员从知识中知道的,但我很高兴知道我是否可以理解仅根据方法签名可以返回 null 值,而无需打开 android 文档) 但是我'令我惊讶的是,编译器并没有阻止我将返回值分配给不可为空的 View 对象。它不应该阻止这项任务吗?ASAIK,以下作业将无法编译:

var str1 : String? = "just a string"
var str2: String = str1

那么区别是什么呢?

标签: javaandroidkotlinnullable

解决方案


Kotlin 的无效性检查是 kotlinc 的虚构。一旦生成了一个类文件,那个问号(或没有问号)就消失了。编译器没有线索。是的,方法签名具有此信息(如果您有一个文件:fun helloWorld(): String { return "Hello" },并编译它,然后编写其他调用helloWorld()的代码,该代码知道 helloworld 函数已被声明为返回非空字符串,即使您没有源文件) - 但就 java-the-VM-executable 而言,这是一条注释。

这样,它与泛型完全相同你可以这样做:

public void iWillMessYouUp(List<String> list) {
    List raw = list; // this generates a warning, but compiles.
    raw.add(5); // this compiles. I just added an Integer to a String list...
}

List<String> myList = new ArrayList<String>();
iWillMessYouUp(myList);
for (Integer i : myList) System.out.println(i);

该代码将在最后一行抛出 ClassCastException,这很有趣,因为该行没有强制转换。你会期望这raw.add(5)条线会抛出一些东西,但事实并非如此,因为泛型是 javac 的虚构。如果 javac 没有在编译时阻止您,那么 java-the-vm 肯定也不会这样做。

那些空类型也是如此。它不是空检查。

但是请注意,kotlin 可能会注入带有 nullcheck 的显式代码,我不记得它是否为了 java 代码的利益而这样做。即这个kotlin代码:

fun say(in: String) {
    println(in)
}

被翻译成伪代码:

METHOD say(Ljava/lang/String;)V {
    if (in == null) throw new NullPointerException("in");
    java.lang.System.out.println(in);
}

快速javap了解 kotlinc 吐出的内容会告诉你它是否这样做。

这使我们陷入无效行为。

事实证明,有 4 种方法可以做泛型。要表示“数字列表”,有 4 种方法可以这样做:

List<Number> // invariant
List<? extends Number> // covariant
List<? super Number> // contravariant
List // raw

最后一个很棘手:它允许一切,并且有意(或多或少)让你破坏东西。它不一定存在,但它确实存在:在泛型存在之前就已经编写了代码,如果原始模式不可用,与遗留代码交互将是痛苦的。

Kotlin 以及所有各种基于注解的 Java 无效系统(它们与 kotlin 所做的一样好,实际上 - 工作原理相同),需要处理这样一个事实,即 95% 的社区代码在类文件形式不包含任何关于 nullity 的提示。因此,它需要被视为“遗产”。

kotlin 解决遗留问题的方式与 java 使用泛型解决遗留问题的方式非常相似:Anything goes

如果您将遗留无效值分配给非 null 事物?'凯。Kotlin 假设您知道自己在做什么。它可以注入一个显式的空检查,但是到处都有很多空检查,太多了,这将对类大小和性能产生显着的影响在旧版空参数的位置传递空值?'凯。显然那没关系,kotlin 不知道并遵从您的代码。

顺便说一句,即使没有遗产,也有 3 个无效,而不是 2 个这一事实应该告诉您,kotlin 的“事物要么可以为空,要么不能”被过度简化了,这意味着 kotlin 在这方面是不完整的。所以它是:不可能编写一个方法来接收 ?? 的列表。nullity 字符串并以任何方式适当地执行(因此您可以传入可空字符串或不可空字符串的列表,您的方法不在乎,同时您仍然可以获取(可空)字符串并添加 Xs,其中 X 是要么是已知的非空字符串,要么是从该列表中获得的(可能)空字符串。不变量、协变和逆变的概念往往适用于所有类型关系和标签,而空值与 ' 一样多是一个标签此列表包含字符串' 是一个。


推荐阅读