一起从零学Kotlin-20170805

学习本文章的同学,你一定得熟悉Java方面的知识,否则本文章不是太适合你哦!

前篇系列文章(请同学们按照顺序来学习):

一起从零学Kotlin-20170728:
http://blog.csdn.net/clandellen/article/details/76283527

一起从零学Kotlin-20170730:
http://blog.csdn.net/clandellen/article/details/76369434

一起从零学Kotlin-20170801:
http://blog.csdn.net/clandellen/article/details/76519661

一起从零学Kotlin-20170803:
http://blog.csdn.net/clandellen/article/details/76602453

0.官方API

链接:http://pan.baidu.com/s/1geVxlJ1 密码:4nht
官方API才是学习的王道……

Intellij IDEA(kotlin开发学习IDEA,并且还支持Java)下载
https://www.jetbrains.com/idea/

官网下载慢的可以从我云盘里面下载:
链接:http://pan.baidu.com/s/1i4Au2nb 密码:qato

1.Java与Kotlin的混合开发

Java和Kotlin可以混合开发,这你肯定是知道的,但是要想Java与Kotlin混合开发规则又是怎么样的呢?本篇文章带你进行学习。

2.Java与Kotlin的基本互操作

2.1 属性的读与写

Kotlin能自动识别Java中类属性的Getter和Setter,Java操作Kotlin属性通过Getter / Setter,这没什么难点。

2.2 空安全类型

Kotlin的空类型是怎样处理的?前面已经讲解了,实际Kotlin会实时的运行一个方法,对变量,参数,返回值等等进行空类型检查,所以kotlin操作Java代码必须分析是否Java的那个变量,参数,返回值能否为空,因为Java不存在Kotlin这种检查空类型的机制。所以需要开发者自己去分析某些位置能不能为空,当然也可以通过Java注解的方式来避免这一问题。@Nullable和@NotNull

2.3 函数的调用

在Kotlin中有一个Java不存在的类型的函数,这种函数就是包级函数,包级函数的所有函数(包括扩展函数)都是静态的,在编译的时候呢,Kotlin就把所有的包级函数都放在了一个类当中,Java可以通过此类来访问到所有的包级函数,这个类的名字默认的就是: Kotlin的文件名+kt,当然也可以通过注解@file:JvmName的方式进行修改,示例代码如下:

包名a.kt

//包名a.kt代码
class Student(var name:String,var age:Int)



fun Student.getMessage(){//这个扩展方法被编译成类"包名akt"中的静态方法public static getMessage(Student student)

    println("学生姓名:${this.name},学生年龄:${this.age}")

}

fun method(i:Int){//这个函数被编译类"包名akt"中的静态方法public static method(int i)

    println(i)

}  

Java中调用:

public static void main(String[] args){

    Student s = new Student("ellen",23);



    包名aKt.getMessage(s);//访问包名akt的扩展函数

    包名aKt.method(3);//访问包名akt的扩展函数

   //还有运算符的重载,这跟扩展方法调用是一样的,注意Java中不能通过运算符进行调用,而是要通过方法的名字



}

2.3 Kotlin中几个注解的使用规则

1.@JavaFiled:将属性编译为Java变量

2.@JvmStatic:将对象的方法编译成Java静态方法,通常应用与伴生对象

3.@JvmOverloads:默认参数生成重载方法

4.@file:JvmName:指定Kotlin文件编译后的类名

2.4 NoArg与AllOpen

这在前面讲到”data”关键字已经进行了说明,被”data”修饰的类是final的,并且是没有无参构造函数,这感觉是个坑,所以Kotlin官方给出了解决方案,就是使用NoArg(无参构造函数)和AllOpen(可被继承)来解决问题。

2.5 泛型

1.Kotlin中的类型通配符不是”?”了,而是”*”,因为”?”在Kotlin当中有其他用途,你已经学过啦

2.协变与异变 out/in

3.没有Raw类型

其他是一样一样的,用法是一样的。

3.SAM转换

SAM 转换,即 Single Abstract Method Conversions,就是对于 只有单个非默认抽象方法接口 的转换 —— 对于符合这个条件的接口(称之为 SAM Type ),在 Kotlin 中可以直接用 Lambda 来表示 —— 当然前提是 Lambda 的所表示函数类型能够跟接口的中方法相匹配。还是拿代码来说事吧!

Java当中有这么一个类,里面存在这一个成员属性和两个方法,这个成员属性是一个ArrayList,两个方法是一个往集合当中放Runnable的子类对象,而另一个移除Runnable子类对象,示例代码如下:

import java.util.ArrayList;

public class RunnableManager {

    private ArrayList arrayList = new ArrayList();


    public void addRunnable(Runnable runnable){

       if(arrayList.add(runnable)) {
            System.out.println("添加[" + runnable + "]到集合成功");
       }

       System.out.println("此时集合当中的总元素个数为:"+arrayList.size());

    }

    public void removeRunnable(Runnable runnable){

        if(arrayList.remove(runnable)){
            System.out.println("从集合中移除["+ runnable +"]成功");
        }

        System.out.println("此时集合当中的总元素个数为:"+arrayList.size());

    }

}

在Kotlin中这两个方法是可以接收Lambda表达式的,为什么呢? 因为Lambda表达式可以作为参数传递给Runnable类型的参数,这种Lambda表达式是有限制的,必须是(…) -> Unit类型的才可以,也就是返回类型是Uint的Lambda表达式都可以,看看下面的代码:

fun main(args: Array) {



    var runnableManager = RunnableManager()//生成Java中的RunnableManager对象

    var runnableLamada = { getString("dsadasda",5)}//Lambda表达式

    runnableManager.addRunnable(runnableLamada)//将上面的Lambda表达式作为参数传递给addRunnable()方法
    runnableManager.addRunnable(runnableLamada)
    runnableManager.addRunnable(runnableLamada)
    runnableManager.addRunnable(runnableLamada)

}

fun getString(str:String,i:Int){

    println(str+":"+i)

}

输出:

注意笔者的输出结果可能与你不一致,因为实例对象的生成在虚拟机内存当中是随机分配的,无需太通过结果我们可以看出每次调用Lambda表达式的引用runnableLamada都会生成一个Runnable的实现的实例对象,那么移除的时候肯定是存在问题的,比如运行以下代码:

fun main(args: Array) {


    var runnableManager = RunnableManager()

    var runnableLambda = { getString("dsadasda",5)}

    println("添加元素:")
    runnableManager.addRunnable(runnableLambda)
    runnableManager.addRunnable(runnableLambda)
    runnableManager.addRunnable(runnableLambda)
    runnableManager.addRunnable(runnableLambda)


    println("\n删除元素:")
    runnableManager.removeRunnable(runnableLambda)//这里调用runnableLambda会生成一个新的实现的Runnable实例对象,所以肯定是移失败的
    runnableManager.removeRunnable(runnableLambda)
    runnableManager.removeRunnable(runnableLambda)
    runnableManager.removeRunnable(runnableLambda)

}

fun getString(str:String,i:Int){

    println(str+":"+i)

}

输出:

由结果可见,元素并没有成功移除掉,为什么呢?我们前面已经说了,每次调用Lambda表达式的引用runnableLamada都会生成一个Runnable的实现的实例对象,所以肯定是移除失败的,如果不信,你在RunnableManager类中修改方法removeRunnable如下所示:

public void removeRunnable(Runnable runnable){

        System.out.println("移除的元素:["+runnable+"]");

        if(arrayList.remove(runnable)){
            System.out.println("从集合中移除["+ runnable +"]成功");
        }

        System.out.println("此时集合当中的总元素个数为:"+arrayList.size());
}

重新运行Kotlin中的代码输出以下结果:
添加元素:

所以啊!在使用SAM的时候一定要注意一点,就是每次作为参数去调用Java里的方法,这个Lambda表达式都会去新建一个实现接口的实例对象。

4.正则表达式

在Kotlin中处理和操作正则表达式有两种方式,第一种使用Java当中封装的正则表达式库,我想这笔者不用讲了吧,第二种使用Kotlin自己的正则表达式工具类,那我们来看看Kotlin当中正则表达式的工具类吧:

先来看看Java当中的一段代码:

public static void main(String[] args){

    String str = "00A11B22C33D44E55F66G77H88I99";//把这个字符串切割,让它只剩下数字,并且存入一个String[]中

    String geShi = "[A-Z]";

    String[] results = str.split(geShi);

    for(String s:results){

        System.out.println(s);

    }

}

把这段代码改写成Kotlin的方式,示例代码如下:

fun main(args: Array) {

    var str = "00A11B22C33D44E55F66G77H88I99"

    var geShi = "[A-Z]" 

    var results = Regex(geShi).split(str)//Regex为Kotlin中处理正则表达式的工具类

    for(s in results){

        println(s)

    }


}  

5.集合框架

Kotlin可以毫无烦恼的使用Java当中的集合框架,示例代码如下:

fun main(args: Array) {

    var arrayList = ArrayList()//java风格

    arrayList.add("1")
    arrayList.add("2")
    arrayList.add("3")
    arrayList.add("4")
    arrayList.add("5")

    arrayList.remove("1")
    arrayList.remove("2")
    arrayList.remove("3")
    arrayList.remove("4")

    for(str in arrayList){

        println(str)

    }


}

Kotlin中的集合是不可变的,什么意思,意思就是说没有添加和删除方法的实现,目前笔者也不知道为何这样设计,或许之后的Kotlin版本应该会改变Kotlin自身集合的一些特性吧,反正目前为止只需要用好Java集合框架即可,以Map集合为例子:

fun main(args: Array) {



    var maps1 = mapOf("a" to 1,"b" to 2,"c" to 3,"c" to 3,"c" to 3,"c" to 3,"c" to 3)
    //这么声明是不能直接调用put和remove等基本操作的方法

    var maps2 = mapOf(1 to 2,2 to 3,3 to 4,4 to 5, 5 to 6,6 to 7,7 to 8)

    //mps2.remove(1)  //这么写是错误的,因为没有remove()方法让你去调用


    for((key,value) in maps1){

        println("Key:${key},Value:${value}")

    }


}

所以呢,你只要会使用Java中的集合框架,那么Kotlin中使用集合框架就不是什么问题了。

6.IO操作

Kotlin中的IO操作还是使用Java IO操作库,只不过呢,Kotlin中封装了一些便利的扩展方法,没有使用扩展方法的代码如下所示:

fun main(args: Array) {

    var br = BufferedReader(FileReader(File("D:\\A.txt")))

    var line = ""


    while(true){//注意这里不要写成 (line = br.readLine())!=null,因为Kotlin当中语句"line = br.readLine()"不再返回line的值

        line = br.readLine()?:break

        println(line)

    }

    br.close()


}  

6.1 使用”use”自动关闭读写文件

fun main(args: Array) {

    var br = BufferedReader(FileReader(File("D:\\A.txt"))).use {

        while(true){

            var line = it.readLine()?:break

            println(line)

        }


    }//执行完use里面的Lambda表达式将会自动执行br.close,这就是use的好处



}

6.2 readLines直接获取文件所有内容

File("D:\\A.txt").readLines().forEach(::println)

7.装箱和拆箱带来的麻烦

比如你要在Kotlin中去实现Java的接口Game,代码如下所示:

public interface Game {

    void getID(int id);

    void getID(Integer id);

}

由于Java的装箱以及拆箱,实现这个Game接口就会出现冲突,这两个getID()方法被实现的时候总是被认为为同一个方法,因为Kotlin中只存在Int类型,所以Kotlin把int id当成了Int id,Integer id也当成了Int id,所以冲突就出现了,你怎么能实现Game接口后,出现两个名字相同,参数类型也相同的方法呢?这就是Java装箱和拆箱带来的麻烦,那么如何解决这中问题呢?解决方式有很多,去修改Game接口是一种,但是怎样在不修改Game接口的情况下解决问题呢?使用Java中间类啊,在Java中让某个中间类去实现这个Game接口,然后在kotlin当中再去继承这个类不就解决问题了吗?示例代码如下:

Java中间类:

public class MyGame implements Game{
    @Override
    public void getID(int id) {

        System.out.println("这是int参数的方法实现");

    }

    @Override
    public void getID(Integer id) {

        System.out.println("这是Integer参数的方法实现");

    }
}

Kotlin中的代码:

fun main(args: Array) {

    var myGame = MyGame_Kotlin()

    myGame.getID(3)


}


class MyGame_Kotlin : MyGame()

你可能感兴趣的:(Kotlin)