为了账号安全,请及时绑定邮箱和手机立即绑定

Kotlin and Java Interoperability

标签:
Kotlin

Calling Java code from Kotlin

Kotlin is designed with Java Interoperability in mind. Existing Java code can be called from Kotlin in a natural way, and Kotlin code can be used from
Java rather smoothly as well. In this section we describe some details about calling Java code from Kotlin.

Pretty much all Java code can be used without any issues:

import java.util.*fun demo(source: List<Int>) {    val list = ArrayList<Int>()    // 'for'-loops work for Java collections:
    for (item in source) {
        list.add(item)
    }    // Operator conventions work as well:
    for (i in 0..source.size - 1) {
        list[i] = source[i] // get and set are called
    }
}

Getters and Setters

Methods that follow the Java conventions for getters and setters (no-argument methods with names starting with get
and single-argument methods with names starting with set) are represented as properties in Kotlin.
Boolean accessor methods (where the name of the getter starts with is and the name of the setter starts with set)
are represented as properties which have the same name as the getter method.

For example:

import java.util.Calendarfun calendarDemo() {    val calendar = Calendar.getInstance()    if (calendar.firstDayOfWeek == Calendar.SUNDAY) {  // call getFirstDayOfWeek()
        calendar.firstDayOfWeek = Calendar.MONDAY      // call setFirstDayOfWeek()
    }    if (!calendar.isLenient) {                         // call isLenient()
        calendar.isLenient = true                      // call setLenient()
    }
}

Note that, if the Java class only has a setter, it will not be visible as a property in Kotlin, because Kotlin does not support set-only properties at this time.

Methods returning void

If a Java method returns void, it will return Unit when called from Kotlin.
If, by any chance, someone uses that return value, it will be assigned at the call site by the Kotlin compiler,
since the value itself is known in advance (being Unit).

Escaping for Java identifiers that are keywords in Kotlin

Some of the Kotlin keywords are valid identifiers in Java: in{: .keyword }, object{: .keyword }, is{: .keyword }, etc.
If a Java library uses a Kotlin keyword for a method, you can still call the method
escaping it with the backtick (`) character:

foo.`is`(bar)

Null-Safety and Platform Types

Any reference in Java may be null{: .keyword }, which makes Kotlin's requirements of strict null-safety impractical for objects coming from Java.
Types of Java declarations are treated specially in Kotlin and called platform types. Null-checks are relaxed for such types,
so that safety guarantees for them are the same as in Java (see more below).

Consider the following examples:

val list = ArrayList<String>() // non-null (constructor result)list.add("Item")val size = list.size // non-null (primitive int)val item = list[0] // platform type inferred (ordinary Java object)

When we call methods on variables of platform types, Kotlin does not issue nullability errors at compile time,
but the call may fail at runtime, because of a null-pointer exception or an assertion that Kotlin generates to
prevent nulls from propagating:

item.substring(1) // allowed, may throw an exception if item == null

Platform types are non-denotable, meaning that one can not write them down explicitly in the language.
When a platform value is assigned to a Kotlin variable, we can rely on type inference (the variable will have an inferred platform type then,
as item has in the example above), or we can choose the type that we expect (both nullable and non-null types are allowed):

val nullable: String? = item // allowed, always worksval notNull: String = item // allowed, may fail at runtime

If we choose a non-null type, the compiler will emit an assertion upon assignment. This prevents Kotlin's non-null variables from holding
nulls. Assertions are also emitted when we pass platform values to Kotlin functions expecting non-null values etc.
Overall, the compiler does its best to prevent nulls from propagating far through the program (although sometimes this is
impossible to eliminate entirely, because of generics).

Notation for Platform Types

As mentioned above, platform types cannot be mentioned explicitly in the program, so there's no syntax for them in the language.
Nevertheless, the compiler and IDE need to display them sometimes (in error messages, parameter info etc), so we have a
mnemonic notation for them:

  • T! means "T or T?",

  • (Mutable)Collection<T>! means "Java collection of T may be mutable or not, may be nullable or not",

  • Array<(out) T>! means "Java array of T (or a subtype of T), nullable or not"

Nullability annotations

Java types which have nullability annotations are represented not as platform types, but as actual nullable or non-null
Kotlin types. The compiler supports several flavors of nullability annotations, including:

  • JetBrains
    (@Nullable and @NotNull from the org.jetbrains.annotations package)

  • Android (com.android.annotations and android.support.annotations)

  • JSR-305 (javax.annotation, more details below)

  • FindBugs (edu.umd.cs.findbugs.annotations)

  • Eclipse (org.eclipse.jdt.annotation)

  • Lombok (lombok.NonNull).

You can find the full list in the Kotlin compiler source code.

Annotating type parameters

It is possible to annotate type arguments of generic types to provide nullability information for them as well. For example, consider these annotations on a Java declaration:

@NotNullSet<@NotNull String> toSet(@NotNull Collection<@NotNull String> elements) { ... }

It leads to the following signature seen in Kotlin:

fun toSet(elements: (Mutable)Collection<String>) : (Mutable)Set<String> { ... }

Note the @NotNull annotations on String type arguments. Without them, we get platform types in the type arguments:

fun toSet(elements: (Mutable)Collection<String!>) : (Mutable)Set<String!> { ... }

Annotating type arguments works with Java 8 target or higher and requires the nullability annotations to support the TYPE_USE target (org.jetbrains.annotations supports this in version 15 and above).

JSR-305 Support

The @Nonnull annotation defined
in JSR-305 is supported for denoting nullability of Java types.

If the @Nonnull(when = ...) value is When.ALWAYS, the annotated type is treated as non-null; When.MAYBE and
When.NEVER denote a nullable type; and When.UNKNOWN forces the type to be platform one.

A library can be compiled against the JSR-305 annotations, but there's no need to make the annotations artifact (e.g. jsr305.jar)
a compile dependency for the library consumers. The Kotlin compiler can read the JSR-305 annotations from a library without the annotations
present on the classpath.

Since Kotlin 1.1.50,
custom nullability qualifiers (KEEP-79)
are also supported (see below).

Type qualifier nicknames (since 1.1.50)

If an annotation type is annotated with both
@TypeQualifierNickname
and JSR-305 @Nonnull (or its another nickname, such as @CheckForNull), then the annotation type is itself used for
retrieving precise nullability and has the same meaning as that nullability annotation:

@TypeQualifierNickname@Nonnull(when = When.ALWAYS)@Retention(RetentionPolicy.RUNTIME)public @interface MyNonnull {
}@TypeQualifierNickname@CheckForNull // a nickname to another type qualifier nickname@Retention(RetentionPolicy.RUNTIME)public @interface MyNullable {
}interface A {    @MyNullable String foo(@MyNonnull String x); 
    // in Kotlin (strict mode): `fun foo(x: String): String?`
    
    String bar(List<@MyNonnull String> x);       
    // in Kotlin (strict mode): `fun bar(x: List<String>!): String!`}

Type qualifier defaults (since 1.1.50)

@TypeQualifierDefault
allows introducing annotations that, when being applied, define the default nullability within the scope of the annotated
element.

Such annotation type should itself be annotated with both @Nonnull (or its nickname) and @TypeQualifierDefault(...)
with one or more ElementType values:

  • ElementType.METHOD for return types of methods;

  • ElementType.PARAMETER for value parameters;

  • ElementType.FIELD for fields; and

  • ElementType.TYPE_USE (since 1.1.60) for any type including type arguments, upper bounds of type parameters and wildcard types.

The default nullability is used when a type itself is not annotated by a nullability annotation, and the default is
determined by the innermost enclosing element annotated with a type qualifier default annotation with the
ElementType matching the type usage.

@Nonnull@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER})public @interface NonNullApi {
}@Nonnull(when = When.MAYBE)@TypeQualifierDefault({ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE_USE})public @interface NullableApi {
}@NullableApiinterface A {    String foo(String x); // fun foo(x: String?): String?
 
    @NotNullApi // overriding default from the interface
    String bar(String x, @Nullable String y); // fun bar(x: String, y: String?): String 
    
    // The List<String> type argument is seen as nullable because of `@NullableApi`
    // having the `TYPE_USE` element type: 
    String baz(List<String> x); // fun baz(List<String?>?): String?

    // The type of `x` parameter remains platform because there's an explicit
    // UNKNOWN-marked nullability annotation:
    String qux(@Nonnull(when = When.UNKNOWN) String x); // fun baz(x: String!): String?}



作者:zbmzly
链接:https://www.jianshu.com/p/2cdc2b920dd3


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消