Kotlin与Java的混合开发

包括基本的互操作行为、SAM转换、正则表达式、集合框架、IO操作:[实际上用到i的还是 java的api,只不过要注意kotlin为我们提供了很多扩展方法]、装箱和拆箱、注解处理器。

下面一一介绍:

1.基本的互操作行为:

1.属性读写:

Kotlin自动识别 Java Getter/Setter

Java操作Kotlin属性通过Getter/Setter

 2. 空安全类型:

Kotlin 空安全类型的原理(java没这个。原理是Kotlin在编译的时候会增加一个函数调用,对参数类型或返回值类型进行为空的检查)

但是因为java没这个,所以kotlin想操作java的代码的话,就会遇到一个平台类型(Platform Type)的问题,这时候就需要我们自己明确

传入的参数或者返回值的类型是不是为空的。

当然开发Java代码的时候也可以使用注解@Nullable 和@NotNull来解决这个问题。

3.几类函数的调用:

包级函数:静态方法(Java没有。)【它在编译的时候会为kotlin生成一个类,这个类包含了所有的高级函数,在Java看来这些都是静态方法。所以在调用的时候只需要按照调用静态方法的方式去调用即可】

扩展方法: 带Receiver的静态方法。【只是增加了一个Receiver参数】

运算符重载: 带Receiver的对应名称的静态方法。

4.几个常见的注解的使用:

@JvmField: 将属性编译为Java变量。【如果我们希望kotlin里面的属性无论是看起来还是编译后都是像Java变量那样,就可以加该注解】

@JvmStatic: 将对象的方法编译成Java静态方法【对于Kotlin的伴生对象或者对象里面的方法,如果我们希望看起来真的像Java里面的静态方法的话,就可加该注解】

@JvmOverloads: 默认参数生成重载方法。【主要i是针对Kotlin中的默认参数。如果一个方法定义了一个默认参数,Java是不能看到它的,Java中并没有默认参数这个概念。如果想让Java享受到这个特性,就可以加该注解来标注这个方法。这样编译器就会为我们生成很多的重载方法,在Java中就可以方便的使用了】

@file: JvmName: 指定Kotlin文件编译后的类名。【像高级函数以及 在某一个文件中为类定义的扩展方法等等,他们在编译之后实际上都会被包含到一个类当中。在Java我们都会通过这个类来访问他们,这个类的名字默认是”Kotlin文件名Kt“,可以通过JVM类来指定这个文件编译后的名字】

5.NoArg 与AllOpen:

这2个插件。dataclass 因为它本身的属性导致它并没有一个默认的无参构造方法,这对于某些ORM框架或者像json这样的需要序列化和反序列化的框架来说是一个障碍。所以官方推出了NoArg插件,它可以在编译的时候为这些类生成一个默认无参构造方法。

其中,NoArg还支持Jpa注解,如 @Entity

而AllOpen插件 是为标注的类去掉final,允许被继承。因为在Kotlin中,所有的类默认是final,无法被继承。这对于像spring他们在编译的时候会为某些类生成它的子类,如果这个类是final的话,这个行为就不会被允许了。因此,官方推出了AllOpen这个插件。

其中,其支持Spring注解,如@Component

还可以支持自定义注解类型,例如@PoKo

6.泛型:【Koltin里面的泛型和Java里的泛型没有太大的差别】

不同之处:

通配符Kotlin的*对应于Java的?     _____这是因为?在Kotion的作用太大了

协变和逆变 out/in  _____由java中的extends和super变成了out/in

例如:ArrayList<out String >

没有Raw类型:_____在Java中Raw类型 的产生主要源于java 1.5之前根本没有泛型这个特性。【Kotlin没有这样的包袱了,所以没有Raw类型】

例如:像Java中的list这种不带泛型参数的写法就称之为Raw类型,对应于Kotlin里面就要写成List<*>


2.SAM转换

实例化Runnable的过程,反编译后会发现一个实例,这个实例实际上就是lambda表达式。

val samInJava = SAMInJava()

samInJava.addTask {

}

说明对于像Runnable这样的Java接口,它只有一个接口方法,那么我们可以在Kotlin中用一个lambda表达式来替代它。在编译的时候,编译器会帮我们把这个lambda表达式变成它。

例如:

Executors.newCachedThreadPool().submit {

}

SAM转换:

Single Abstract Method

SAM转换的条件:

 java接口,单一接口方法。

注意转换后的实例变化。每次用lamda转换成runnable之后,runnable的实例都是不同的。


3.正则表达式

在Kotlin中用Raw字符串定义正则表达式。【比Java方便很多,少了很多的转义】

Java的Pattern在kotlin中也可以直接使用。

或者直接使用Kotlin中的Regex写出更符合kotlin风格的代码。

3.1 在Java中这么用正则表达式写一个例子:

public class Main {
    public static void main(String... args) {
        String source = "Hello, This my phone number: 010-12345678. ";
        String pattern = ".*(\\d{3}-\\d{8}).*";
        Matcher matcher = Pattern.compile(pattern).matcher(source);


        while(matcher.find()){
            System.out.println(matcher.group());
            System.out.println(matcher.group(1));
        }
    }
}

3.2 在Kotlin中的等效的写法:(照抄的java)

fun main(args: Array<String>) {
    val source = "Hello, This my phone number: 010-12345678. "
    val pattern = """.*(\d{3}-\d{8}).*"""
    val matcher = Pattern.compile(pattern).matcher(source)


    while (matcher.find()) {
        println(matcher.group())
        println(matcher.group(1))
    }
}

3.3 kotlin也有自己的方式处理正则表达式:[kotlin自身的正则表达式]

fun main(args: Array<String>) {
    val source = "Hello, This my phone number: 010-12345678. "
    val pattern = """.*(\d{3}-\d{8}).*"""


    Regex(pattern).findAll(source).toList().flatMap(MatchResult::groupValues).forEach(::println)

}

findAll()返回的是一个软序列。flatMap(MatchResult::groupValues)相当拿到了一个groupvalues的集合。Kotlin对上面的转义的支持,比如java中需要把\d中的斜杠转移正正则表达式中的斜杠,即写为//d。但是kotlin中有RawString,所以上面的代码不用考虑转义斜杠了。


4.集合框架

kotlin没有自己造轮子,所以它可以用java里面中的所有东西。

比如:java中的一个问题,我们remove(0),假设这个list存放的是整型,那我们到到底是remove索引为0的元素还是remove掉list中0这个整型对象呢。

针对此,kotlin改善了下。通过索引remove掉,就写为lsit.removeAt(int index).;list.remove(obejct)

 【::println是针对println函数的引用。其他的以次类推。】

集合注意可变和不可变。【不可变的集合返回的是kotlin内部它自定义的一个集合。因此我们无权对它进行put等操作。但是Java调用不可变的时候它统一可变的,假设此时在java中调用它并add等操作就会报错,因为它返回的List根本就没实现add和put等操作】

这些可变和不可变的集合都会被映射成java中对应的集合。

需要注意的细节:

Kotlin到Java的类型映射。

不可变与可变集合接口。

kotlin对这些集合框架部分接口做了优化。比如remove和removeAt


5.IO操作

File、Stream、Reader、Writer的扩展方法.

使用use扩展自动关闭资源。

小文件readLines()、readText()、readBytes()一次性读写操作。

5.1 用java读写一个文件的例子:

public class IO {
    public static void main(String... args) {
        BufferedReader bufferedReader = null;
        try {
             bufferedReader = new BufferedReader(new FileReader(new File("build.gradle")));
            String line;
            while((line = bufferedReader.readLine()) != null){
                System.out.println(line);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                bufferedReader.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5.2 Kotlin照抄上面的代码:

fun main(args: Array<String>) {
    val file = File("build.gradle")
    val bufferedReader = BufferedReader(FileReader(file))
    var line: String


    while(true){
        line = bufferedReader.readLine()?:break
        println(line)
    }
    bufferedReader.close()
}

注意line = bufferedReader.readLine()?:break中的line = bufferedReader.readLine()它实际上是一个赋值语句,它并没有把line的值作为返回值。这个和java不一样。所以不能在while中直接写 while((line = bufferedReader.readLine()) != null),所以上面的代码直接写为while(true)了。

5.3 Kotlin中的先进写法:【传统写法常常忘记close】

          解决这个问题,Closeable可以为 我们自动关闭,use实际上就是这个东西的扩展方法了。它可以为我们closeable接口的实例来提供自动关闭的操作。

fun main(args: Array<String>) {
    val file = File("build.gradle")
    BufferedReader(FileReader(file)).use {
        var line: String
        while(true){
            line = it.readLine()?:break
            println(line)
        }
    }
}

所以写在use里面的不用我们去手动关闭:

对于一些小文件,kotlin提供了一些更简单的方法,例如:


fun main(args: Array<String>) {

    File("build.gradle").readLines().forEach(::println)

}

6.装箱和拆箱

了解kotlin基本类型到java的映射关系。【映射是编译器自动帮我们去做的】

注意规避基本类型相关的的问题【因为kotlin不再区分java中的基本类型和装箱类型】

kotlin中的int 类型和Integer装箱类型 合二为一变为 ”Int“(float、 double也是)

.java:
public interface BoxIf1 {
    String get(int key);


    String get(Integer key);
}

在kotlin中去实现它:

class BoxImpl1: BoxIf1 {

    override fun get(key: Int): String {

        return "Hello"

    }

//    override fun get(key: Int?): String {

//        return "Hello"

//    }

}

看上去没什么问题。

 

再看另外一个接口:

public interface BoxIf2 extends Map<Integer, String> {


    String get(int key);


    String get(Integer key);


}

然后kotlin中去实现它:

class BoxImpl2: BoxIf2 {
    override val size: Int
        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.


    override fun containsKey(key: Int?): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun containsValue(value: String?): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun get(key: Int): String? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override operator fun get(key: Int): String? {
        return "Hello"
    }


    override fun isEmpty(): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
    override val entries: MutableSet<MutableMap.MutableEntry<Int, String>>
        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
    override val keys: MutableSet<Int>
        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.


    override val values: MutableCollection<String>
        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.


    override fun clear() {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun put(key: Int?, value: String?): String? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun putAll(from: Map<out Int, String>) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun remove(key: Int?): String? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}

发现会爆红。

因为kotlin中的Map把get()_方法变成了operator方法,并且参数Key和java中有不同之处。java中的get()的参数是object。这实际上是kotlin编译器为我们做的一个优化。所以在此处没办法写出一个合适的方法满足这个接口。

 override fun get(key: Int): String? {
//        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
//    }
//
//    override operator fun get(key: Int): String? {
//        return "Hello"
//    }
//

这2个get方法冲突。所以用koitlin不能直接去实现这个接口。

这个时候只能写一个中间的java类(复写get方法,那么kotlin就不会去访问它了)。让kotlin直接去继承这个类来解决这个问题:

public abstract class AbsBox implements Map<Integer, String> {


    public String get(Object key) {
        return null;
    }
}

kotlin去继承这个中间类:

class BoxImpl3: AbsBox() {
    override fun get(key: Int): String {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override val size: Int
        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.


    override fun containsKey(key: Int?): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun containsValue(value: String?): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun isEmpty(): Boolean {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override val entries: MutableSet<MutableMap.MutableEntry<Int, String>>
        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
    override val keys: MutableSet<Int>
        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.
    override val values: MutableCollection<String>
        get() = TODO("not implemented") //To change initializer of created properties use File | Settings | File Templates.


    override fun clear() {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun put(key: Int?, value: String?): String? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun putAll(from: Map<out Int, String>) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


    override fun remove(key: Int?): String? {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }


}

7.注解处理器

apply plugin: "kotlin-kapt"

注意添加生成的代码到SourceSets、

注意IntelliJ有些还不支持注解处理器的编译,需要在右侧的gradle手动点击build。

interface GitHubService{


    @GET("/repos/enbandari/Kotlin-Tutorials/stargazers")
    fun getStarGazers(@Query("page") page: Int = 1, @Query("per_page") pageSize: Int = 20): Call<List<User>>
}


object Service {
    val api: GitHubService by lazy {
        DaggerRESTFulComponent
                .builder()
                .build()
                .retrofit()
                .create(GitHubService::class.java)
    }
}


fun main(args: Array<String>) {
    Service.api.getStarGazers().execute().body().map(::println)
}

上述代码Dagger要i使用到注解处理器:

在build.gradle中添加:apply plugin: "kotlin-kapt"

然后build,可以在build文件夹下为我i们生成了很多文件。

并在build.gradle中添加这些文件的路径:

sourceSets{    main.kotlin.srcDirs += "build/generated/source/kapt/main"}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒不了的星期八

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值