Kotlin-Kotlin中调用Java

版权声明:看上的,随便转载。 https://blog.csdn.net/Android_app/article/details/75003872

Kotlin在设计的时候就考虑到了和Java的交互,在现有的情况下Kotlin本身就可以调用java代码,并且Kotlin代码也可以在Java中正常使用,在本篇中,我们将讨论在Kotlin中调用java代码的一些细节。

完美的使用java代码没有任何问题:

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
    }
}

Getter 和Setter

getter和setter方法都遵从java习惯(方法名以get开头的无参方法和方法名以set开头的只有一个参数的方法),这些都还出现在kotlin的属性中,例如:

import java.util.Calendar

fun calendarDemo() {
    val calendar = Calendar.getInstance()
    if (calendar.firstDayOfWeek == Calendar.SUNDAY) {  // call getFirstDayOfWeek()
        calendar.firstDayOfWeek = Calendar.MONDAY       // call setFirstDayOfWeek()
    }
}

==注意:如果java类中只有setter,那么在kotlin就不能作为一个属性,因为kotlin不支持只有setter的属性==

返回void的方法

如果在Java中是返回void,那么在kotlin中就是返回Unit,如果有任何改变,某些改变还会反应在返回值中,kotlin编译还是把它赋值到调用处,因为值本身就是预先知道的.

不可忽略在java中没有的Kotlin关键字

有一些kotlin中的关键字如in,object,is等,在java中是无效的,如果java库中应用到了kotlin的关键字,你可以通过使用后撇号(`)来包裹关键字后再继续使用

foo.`is`(bar)

空安全和平台类型

在Java中的所有引用都可以为null,Kotlin中严格要求所有对象都是零风险的空安全就是来自java的这一思想,在kotlin中处理java中的声明类型被称为平台类型,空检查就是这么一种类型,所以它们的安全保障和在java中是一样的。

请思考下面的例子:

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)

当我们从平台类型的变量中调用方法的时候,kotlin在运行时并没有标识空异常,但在运行时却有可能调用失败,因为空指针或断言会让kotlin产生防止空传播。

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

平台类型是不可表示的,意思就是不能用语言明确的把它们写下来,当一个平台类型的值赋值给kotlin变量时,我们可以依赖类型推断(每个平台类型都可以推断成变量,像上面那个例子的item),或者我们可以选择我们期望的类型(可为空或非空类型都可以):

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

如果我们选择非空类型,编译器会明确执行一些断言,这可以避免kotlin的非空变量变成空引用,当我们传递平台值给kotlin的时,希望是非空值时,断言就会执行,整体上,编译器会尽最大努力的防止空传递经过程序。

平台类型的标记

前面章节中有提到,在程序中不能明确指定平台类型,因此在语言中没有想关它们的语法,然而,编译器和IDE有时候还是需要显示它们(在错误信息、参数信息等),因此我们有一个助记符:
- T! 意思就是’T’ 或者’T?’
- (Mutable)Collection! 意思就是Java集合中的T可能会变,有可能为空
- Array<(out) T>! 意思就是Java数组中的T(或者是T的子类型),有可能为空

可为空的注解

java中可为空的注解不会出现在平台类型中,但事实上Kotlin都有可为空的类型或非空类型,编译器支持几种可为空的注解,包含以下:
- JetBrains(@Nullable和@NotNull,位于org.jetbrains.annotations包下)
- Android(com.android.annotations和android.support.annotations)
- JSR-305(javax.annotation)
- FindBugs (edu.umd.cs.findbugs.annotations)
- Eclipse(org.eclipse.jdt.annotation)
- Lombok (lombok.NonNull)

可以在Kotlin编译器源码中找到全部.

映射类型

Kotlin对待Java的类型有些特别,有些类型没有直接从Java转过来,但还是有相对应的映射类型,这些类型会在编译时就会匹配,在运行时就会保持不变,Java的基本类型也有对应的Kotlin类型相对应:

Java 类型 Kotlin 类型
byte kotlin.Byte
short kotlin.Short
int kotlin.Int
long kotlin.Long
char kotlin.Char
float kotlin.Float
double kotlin.Double
boolean kotlin.Boolean

一些非基本类型也有对应的映射:

Java 类型 Kotlin 类型
java.lang.Object kotlin.Any!
java.lang.Cloneable kotlin.Cloneable!
java.lang.Enum kotlin.Enum!
java.lang.Comparable kotlin.Comparable!
java.lang.Annotation kotlin.Annotation!
java.lang.Deprecated kotlin.Deprecated!
java.lang.CharSequence kotlin.CharSequence!
java.lang.String kotlin.String!
java.lang.Number kotlin.Number!
java.lang.Throwable kotlin.Throwable!

Java的基本类型包装类对应于可为空的Kotlin类型:

Java 类型 Kotlin 类型
java.lang.Byte kotlin.Byte?
java.lang.Short kotlin.Short?
java.lang.Integer kotlin.Int?
java.lang.Long kotlin.Long?
java.lang.Character kotlin.Char?
java.lang.Float kotlin.Float?
java.lang.Double kotlin.Double?
java.lang.Boolean kotlin.Boolean?

==注意:基本类型的包装类通常被用来平台类型的映射,例如List

在Kotlin中的Java泛型

Kotlin的泛型跟Java的泛型有一点点的不同,当把Java类型导入到Kotlin的时候我们会做一些转化:
- Java的通配符会转为类型推断
- Foo

if (a is List<Int>) // 错误: cannot check if it is really a List of Ints
// but
if (a is List<*>) // OK: no guarantees about the contents of the list

Java数组

在Kotlin中的数组是不变的,不像Java,这意味着在Kotlin中不要求我们把Array赋值给Array,如果强制这么做,可能会导致运行失败.在kotlin中是禁止把子类的数组转化为超类的数组,但在java这种方式是可以的.

在Java平台上,基本类型的数组是为了避免自动装箱和自动拆箱的消耗,虽然kotlin隐藏了具体细节,但还是需要暴露方法给java代码调用,每个基本类型数组都有特殊的类(如IntArray,DoubleArray,CharArray等)去处理这种情况,他们没有相关的Array类,并且需要在编译的时候降为基本类型以达到最好性能。

假设这里有一个java方法接受数组的索引:

public class JavaArrayExample {

    public void removeIndices(int[] indices) {
        // code here...
    }
}

在Kotlin中你传递基本类型数组,你可以这样做:

val javaObj = JavaArrayExample()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndices(array)  // passes int[] to method

当编译成JVM字节码的识货,编译器的优化就会让数组看起来不太像之前介绍的那样:

val array = arrayOf(1, 2, 3, 4)
array[x] = array[x] * 2 // no actual calls to get() and set() generated
for (x in array) { // no iterator created
    print(x)
}

如果我们找到一个指数,它也不会像前面介绍的那样:

for (i in array.indices) { // no iterator created
    array[i] += 2
}

最终,内部的类型检查(in-checks)也没有像前面那样检查:

if (i in array.indices) { // same as (i >= 0 && i < array.size)
    print(array[i])
}

Java中的可变参数

java的方法有时候会用到可变参数

public class JavaArrayExample {

    public void removeIndices(int... indices) {
        // code here...
    }
}

有另外一种情况,你可能需要范围操作符来传递IntArray:

val javaObj = JavaArray()
val array = intArrayOf(0, 1, 2, 3)
javaObj.removeIndicesVarArg(*array)

这是目前不可能通过一个声明为可变参数的中间方法

运算符

因为Java中没有标记方法的方式,所以它才要用运算符语法,在Kotlin中,只要Java方法有正确的名字和签名就可以进行操作符重载和其他操作(invoke()等),调用Java方法是不可以使用中缀调用语法。

异常检测

在Kotlin中,所有异常都是不可检测的,这就意味着编译器不会强制你捕获异常,因此,当你调用那些有声明异常的java方法时,Kotlin也不会强制你做任何事:

fun render(list: List<*>, to: Appendable) {
    for (item in list) {
        to.append(item.toString()) // 在这里 Java 需要我们捕获IOException 
    }
}

Object的方法

当java类型导入到kotlin中,所有类型都被当做是java.lang.Object类型导入到Any中,因此Any不是平台特定类型,它的成员方法只有toString(),hashCode()和equals(),因此要使java.lang.Object的其他成员方法有效,Kotlin就要使用拓展函数.

wait()/notify()

在【Effective Java】一书第69章中,友好的提示并发时需要用到wait()和notify(),然而,这些方法在Any类型的引用中是无效的,如果你真的需要调用它们,你可以把它转型为java.lang.Object对象来使用

(foo as java.lang.Object).wait()

getClass()方法

如果要检索Object的java类,要用到class引用的java 拓展属性

val fooClass=foo::class.java

这个代码就是使用前面提到的绑定类引用一节中所说的,它从版本1.1开始支持,当然你也可以使用javaClass的拓展属性

val fooClass=foo.javaClass

clone()

如果要覆写clone()方法,那么你的类就得继承kotlin.Cloneable:

class Example : Cloneable {
    override fun clone(): Any { ... }
}

finalize()

如果需要覆写finalize(),你只需要简单的声明,并不需要使用override关键字:

class C {
    protected fun finalize() {
        // finalization logic
    }
}

根据Java的开发原则,finalize()必须不可以被private修饰

访问静态成员

Java类的静态成员是这些类的’同级对象’,虽然我们不可以把这些’同级对象’当成值来使用,但是我们可以明确的访问这些成员,例如:

if (Character.isLetter(a)) {
    // ...
}

Java的反射

Java的反射可以工作再Kotlin类中,反之亦然,在前面提到的那样,你可以使用instance::class.java,ClassName::class.java或instance.javaClass来获得java反射中的java.lang.Class.

其他情况就需要java中有getter何setter方法或有kotlin中的隐性属性,有java字段的KProperty,java方法,或KFunction的构造方法

kotlin中使用JNI

如果要声明一个实现本地方法(C/C++),你可以使用external来修饰:

external fun foo(x: Int): Double

其他的相关实现就和java中一样应用

猜你喜欢

转载自blog.csdn.net/Android_app/article/details/75003872