在Kotlin项目中使用的传统Java库中保持零安全

假设我在旧/旧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代码中使用这个类,并将所有的构造函数参数设置为非空值时,一切都很好:

 val foo = JavaClass("first string", "second string", "third string") println("Value1: ${foo.notNullString.length}") println("Value2: ${foo.nullableString?.length}") println("Value3: ${foo.unannotatedString.length}") 

第一个值是非空的,所以我可以在没有安全调用的情况下访问它。 第二个值,我需要使用安全调用(nullableString?.length),如果没有,我有一个编译时错误,迄今为止好。 在第三个值(unannotatedString)我可以使用它没有一个安全的调用,它编译好。

但是,当我将第三个参数设置为“空”我没有得到一个编译时错误(没有安全调用所需,只有运行时NullPointerException:

 val bar = JavaClass("first string", "second string", null) println("Value4: ${bar.unannotatedString.length}") // throws NPE 

这是预期的行为? Kotlin的编译器是否将注释的Java方法与使用@NotNull注释的相同?

Kotlin视图中的那个变量的类型将是String! ,这是一个平台类型 。

他们最初使得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) { ... } } 

每当Kotlin编译器不知道类型的可空性是什么时,类型就变成了平台类型 ,用一个单一的!

 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中的任何引用都可能为空,这使得Kotlin对来自Java的对象的严格无效安全的要求是不切实际的。 (…)当我们调用平台类型变量的方法时,Kotlin在编译时不会发出可空性错误,但是调用可能在运行时失败,因为空指针异常或Kotlin生成的断言来防止null传播:

 val item = list[0] // platform type inferred (ordinary Java object) item.substring(1) // allowed, may throw an exception if item == null