假设我在旧/遗留Java库中有特定代码:
public class JavaClass {
private String notNullString;
private String nullableString;
private String unannotatedString;
public JavaClass(@NotNull String notNullString,
@Nullable String nullableString,
String unannotatedString) {
this.notNullString = notNullString;
this.nullableString = nullableString;
this.unannotatedString = unannotatedString;
}
@NotNull
public String getNotNullString() {
return notNullString;
}
@Nullable
public String getNullableString() {
return nullableString;
}
public String getUnannotatedString() {
return unannotatedString;
}
}
使用@NotNull和@Nullable注释(使用jetbrains.annotations)正确注释前两个参数。第三个( unnanotatedString )没有正确的注释。
当我在我的Kotlin代码中使用这个类并将所有构造函数参数设置为非null值时,一切都很好:
val foo = JavaClass("first string", "second string", "third string")
println("Value1: ${foo.notNullString.length}")
println("Value2: ${foo.nullableString?.length}")
println("Value3: ${foo.unannotatedString.length}")
第一个值是非null,所以我可以在没有安全调用的情况下访问它。第二个值我需要使用安全调用(nullableString?.length),如果没有,我有一个编译时错误,到目前为止一直很好。在第三个值(unannotatedString)上,我可以在没有安全调用的情况下使用它,它编译得很好。
但是当我将第三个参数设置为“null”时,我没有得到编译时错误(不需要安全调用,只有运行时NullPointerException:
val bar = JavaClass("first string", "second string", null)
println("Value4: ${bar.unannotatedString.length}") // throws NPE
这是预期的行为吗? Kotlin的编译器是否将未注释的Java方法与使用@NotNull注释的方法相同?
答案 0 :(得分:7)
Kotlin视图中该变量的类型为String!
,即platform type。
他们最初使每个变量都来自Java可空,但是他们后来在语言设计过程中改变了这个决定,因为它需要太多的null
处理并且需要太多的安全调用来扰乱代码。
相反,您需要评估来自Java的对象是否为null
,并相应地标记其类型。编译器不会对这些对象强制执行null安全性。
作为一个额外的例子,如果您从Java重写方法,那么参数将再次成为平台类型,无论您是否将它们标记为可空,它都取决于您。如果你有这个Java接口:
interface Foo {
void bar(Bar bar);
}
然后这些都是Kotlin的有效实现:
class A : Foo {
fun bar(bar: Bar?) { ... }
}
class B : Foo {
fun bar(bar: Bar) { ... }
}
答案 1 :(得分:5)
每当Kotlin编译器不知道类型的可空性是什么时,类型变为platform type,用单个!
表示:
public String foo1() { ... }
@NotNull public String foo2() { ... }
@Nullable public String foo3() { ... }
val a = foo1() // Type of a is "String!"
val b = foo2() // Type of b is "String"
val c = foo3() // Type of c is "String?"
这意味着,“我不知道类型是什么,你可能需要检查它”。
Kotlin编译器不对这些类型强制执行空值检查,因为它可能是不必要的:
Java中的任何引用都可能为null,这就是Kotlin的要求 对于来自Java的对象,严格的null安全性是不切实际的。 (......) 当我们在平台类型的变量上调用方法时,Kotlin没有 在编译时发出可空性错误,但调用可能会失败 运行时,因为空指针异常或断言 Kotlin生成以防止空值传播:
val item = list[0] // platform type inferred (ordinary Java object) item.substring(1) // allowed, may throw an exception if item == null
答案 2 :(得分:0)
Java代码可以使用注释传递有关可空性的信息:
@Nullable字符串->被视为字符串?在科特林
@NotNull字符串->在科特林中被视为字符串
不存在注释时,Java类型在Kotlin中成为平台类型。平台类型是Kotlin没有可空性信息的类型-您可以将其视为可空或非空类型。这意味着您对使用此类型执行的操作承担全部责任(就像在Java中一样)。编译器将允许所有操作。与Java中一样,如果对null值执行非null安全的操作,则会获得NPE。
如果Kotlin将Java的所有传入值都视为可空值,我们可以避免进行空值检查,但是最终将产生大量冗余的空值检查,这些值永远不能为空值,因此Kotlin设计人员提出了平台类型。
请注意,您不能在Kotlin中声明平台类型,它们只能来自Java。 String!符号是Kotlin编译器表示平台类型的方式,它强调该类型的可空性是未知的。您不能在Kotlin代码中使用此语法。
根据要如何处理可能的空值,您可以使用以下运算符:
字符串->调用带有参数的非空安全操作 可能为null不允许,将由编译器
字符串?->您可以对其执行的一组操作受到限制 通过编译器,如果您不想传递可为空的值,则表示 强制处理它(比较它将为空值->编译器将 记住这一点,并在范围内将值视为非空值
字符串?。->(安全呼叫运营商)(如果您尝试使用的值) 调用该方法不为空,则该方法正常执行,否则 呼叫被跳过,并且返回null
字符串?->( Elvis运算符,也是非Coalescing运算符) 运算符取两个值,如果是,则结果为第一个值 不为空或第二个(如果第一个为空)
字符串!->(非空断言)-对于null值,将引发异常
?. let ->(让功能与安全呼叫操作员一起)-让 函数仅针对非null值被调用,否则没有任何反应