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
orT?
",(Mutable)Collection<T>!
means "Java collection ofT
may be mutable or not, may be nullable or not",Array<(out) T>!
means "Java array ofT
(or a subtype ofT
), 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 theorg.jetbrains.annotations
package)Android (
com.android.annotations
andandroid.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
andWhen.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; andElementType.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 theElementType
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
共同学习,写下你的评论
评论加载中...
作者其他优质文章