kotlin使用初体验

作者: 锋ivy | 来源:发表于2017-07-10 11:12 被阅读376次

    google宣布Kotlin作为andorid一级开发语言有一段时间了。在这段时间,我也在新的模块上尝试使用了kotlin进行开发,经过这一段时间的开发,我觉得在开发中使用kotlin是个很棒的选择。

    使用Kotlin的很容易,只需要进行几步简单的设置

    • android studio安装下面两个插件,其中Parcelable Code Generator不是必要的,主要用于序列化Parcelable,所示最好也安装一下
      image.png
    • build.grade文件中添加依赖compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
    • build.grade文件中应用插件
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'
    

    通过以上的几步,就可以开始我们的kotlin之旅了

    1. 变量

    kotlin中变量使用var、val来表示,var表示可变变量,val表示不可变变量(相当于java当中的final)。如下所示

    var name: String="ivy"
    val age=12
    

    kotlin定义变量时可以不指定变量类型kotlin会根据你的赋值进行判断该变量的类型,如例子所示会自动把age设置为Int类型,这里需要注意的是如果需要把变量赋值为Null(var name:String?="ivy"),需要加上号才表示该变量可以为Null

    2. 函数

    kotlin方法的使用和java的类似,当没有返回值的时候可以不写,或者写Unit

    fun add(one: Int , two: Int) : Int{
        return one+two
    }
    

    同时,函数还可以为每个参数设置默认值,这样就可以避免多参数时定义多个方法,如下所示。

    fun add(one:Int , two: Int ,three: Int=0){
      //当参时没有设置three的值时,three默认为0
    }
    

    这里有一个比较重要的概念。在kotlin当中函数是一级公民,即函数也是可以作为变量、返回值来使用的,如下所示

    val add=fun (a: Int,b: Int){ println(a+b) }
    add(1,2)
    

    可能有的小伙伴就要说,这有什么卵用,我直接写个方法,然后调用方法不就好了。是,如果只是简单的调用是可以,那如果把函数作为方法的参数,这在java就无法做到了,而把函数作为参数能给我们带来很大的方便,代码看起来也更有逻辑性。如下所示

    fun judgeHasMoney(doHappyThing: ()->Unit){
            if (User.hasMoney()){
                println("no money")
            }else{
                doHappyThing()
            }
    }
    judgeHasMoney(BuyCompany())
    judgeHasMoney(BuyPhone())
    

    这就可以非常方便对能否能购买东西进行判断,代码少了,看上去也更有逻辑性。还有很多函数作为一级公民好处的实例,这里就不一一演示了,for循环、ListMap之类的太量操作符也是利用了这个特性进行实现。

    3. 类的定义

    class Boy(var name: String) : Person, interface{
            init {
                
            }
            constructor(name:String,age: Int) : this(name) {
                
            }
        }
    

    如例子所示,这就是kotlin中类的定义。无论是接口还是父类都是写在冒号后面,用逗号分割,不再使用extendsimplement。在kotlin中,分主构造函数和次构造函数,主构造函数就是类名Boy括号后面参数(如果想在类中直接使用构造函数的参数,需要加上val或者var),次构造函数必须以constructor开头,并且必须继承调用主构造函数,所有的构造函数初始化后都会调用 Init{}方法,所以可以在Init()当中进行初始化操作。
    kotlin类中还有一些需要注意的:

    • 1 在Kotlin中,所有的方法都是不开放的(即子类无法重写),如果子类要重写父类的方法,需要在父类方法前面加上open
    • 2 如果继承了两个类中含有共同的方法,可以通过super<类名>.方法名进行调用
    class SuperMan(): Person(), Boy {
            override fun run(){
                super<Person>.run()
            }
        }
    
    • 3 生成一个对象很简单,直接var girl=Girl()就可以,不再需要new一个妹子了

    4. 空安全

    android开发中,空指针异常是最让我们头痛的也是导致应用崩溃率最高的问题。我们之前已经提到过,在kotlin中,在定义变量的时候就需要指定变量是否允许为空,这在一定程度上帮助我们减少空指针异常的可能性。但是还是不能完成避免空指针的出现,因为有些变量就是可空可不为空,当我们调用时,就有可能因为没有做出判断而出现空指针异常。kotlin也考虑到这个问题,所以在调用可为空的变量时,kotlin会要求我们加上号,如下所示:

    var school?=Class()
    school?.grade?.class?student?.size()
    

    如果我们想查找一个班的学生的人数,可以使用以上的方法,就可以获取到学生的人数,当中的任何一个变量为空,后面的都不会继续执行,这个链式调用就会返回null,避免任何一个阶段为空出现NPE的问题。

    5. List、Map的使用

    在开发中,ListMap是我们最经常用到的类了。在kotlin中,ListMap也分为可变和不可变

    var a = listOf(4)
    var b = mutableListOf<String>()
    var c = mapOf(Pair("3","4"))
    var d = mutableMapOf<String,String>()
    

    不可变的概念即和数组类似,只能够改变每个位置的值,不能减少或者增加size
    遍历List有三种比较常用的方法

    for(position in list.indices){
             //每一个位置遍历
     }
           
    for(str in list){
           //每一个对象遍历    
    }
    
    for((position,str) in list.withIndex()){
           //每一个位置和对象遍历     
    }
    

    遍历Map比较常用的方法

    for((key,value) in map){
                
     }
    

    kotlin的遍历方法相对于java来说,还是很方便和实用的,但是!!!这不是最好用的!!最方便的是kotlin加入了一系列的操作符,使用Rx的小伙伴应该知道操作符有多棒。下面我就来演示一个例子,查找一个班里面年龄超过15岁的女生名字
    java:看我的

    for(student in students){
       if(student.getSex()=="girl"&&student.getAge>15){
            System.out.println(student.getName())
        }
    }
    

    kotlin:小样,看我的

    students.filter{ it.sex=="girl" }
            .filter{ it.age>15 }
            .forEach{ print(student.name") }
    

    是不是整个逻辑一目了然。但是,有些喜欢挑事的小伙伴可能就会说我觉得上面那个没有很方便啊。我就喜欢上面那个。
    好,那我就再给你举两个例子,你写java代码对比一下

    • 1 15岁以上的女生是否有单身的!!!
     students.filter { it.sex=="girl" }
                    .filter { it.age >15 }
                    .any{ !it.hasBoyFriend }
    
    • 2 把15岁以上的女生年龄排序一下,打印出名字
    students.filter { it.sex=="girl" }
                    .filter { it.age >15 }
                    .sortedBy { it.age }
                    .forEach { print(it.name) }
    

    还有其它更棒,更好用的操作符,大家可以查看源码。操作符可以大大提高开发速度,我在这里就不一一展示了(Map一样有各种好用的操作符)

    6. 流程控制

    • 1 for语句的使用和java的类似,也包含了很多的操作符(和List类似)
    for(i in 0 .. 100){
         //0到100       
    }        
     for(i in 0 until 100){
          //0到99      
    }    
    for (i in 100 downTo 0){
         //100到0           
    }
    
    • 2 在java中,when语句中的case判断只能是int,后来也支持了String,但是支持的类型还是很少。而且,只支持相同的类型,这样就导致我们在开发中遇到复杂业务的时候,往往还需要在case中编写if语句,这就给我带来了很大的麻烦,而且代码也比较混乱。但是kotlinwhen语句很好的解决了这个问题。当when(x)有参数时,可以在参数中添加参数所属类型的表达式。当when不带参数时,可以添加各种表达式和方法,而不只是常量。
    when (x) {
                1 -> print("x == 1")
                2,3 -> print("x == 2 or 3")
                in 10..20 -> print("x is in the range")
                else -> { // 注意这个块
                    print("x is shen me gui")
                }
            }
    
    var x:Int=0
            when {
                x==1 -> print("x == 1")
                x in 10..20 -> print("x is in the range")
                isBigNum(x) -> print("big")
                else -> { // 注意这个块
                    print("x is shen me gui")
                }
            }
    
    • 3 在循环中,java经常会用到break跳出循环,那kotlin是怎么实现的呢?请看例子,可以通过直接标签的形式,回到指定位置,和java类似
    tag@ for (i in 0..100) {
      for (j in 0..100) {
        if (i==j)
          break@tag
      }
    }
    

    7. 数据类

    相信java的小伙伴从刚开始学java的时候就知道建立对象先写个javabean,要写get、set方法,比较的时候要重写equal等方法,copy对象的时候要实现Cloneable接口。非常的麻烦,而且容易出错。kotlin给出了一种简单的方法
    data class person(var age: int ,var name: String)
    编绎器会自动使该Bean拥有以下特性:

    • 1get/set方法,当var改为val时,则没有set方法
    • 2 equals()/hashCode()
    • 3 toString() 方法,格式 person(age=18, name=ivy)
    • 4 copy()拷贝功能

    8. 静态变量、静态方法

    kotlin中,没有静态方法的说法。都是通过伴生对象companion object来实现。使用静态变量可以通过
    const val staticName: String ="123"来实现(必须写在类的顶部),一般用于无法确定归属的全局变量。但是最好使用伴生对象,对静态变量归类到所属的类中。使用方法如下

    class TestActivity{
      companion object{
            var username: String="ivy"
            fun startActivity(context: Context):Unit{
                val intent=Intent(context,KotlinHomeActivity::class.java)
                context.startActivity(intent)
            }
        }
    }
    //调用
    TestActivity.companion.startActivity(context)
    

    伴生对象并不是真正意义上的静态变量,本质也是一个对象

    kotlin的单例写法如下:

    companion object{
            fun getUser() : String{
                return UserManager.user
            }
    
            private object UserManager{
                val user=User()
            }
        }
    

    9. 泛型

    kotlin的泛型和java的类似,有以下几点不同:

    • 1 可以通过T::class来获取到T的具体类型
    • 2 kotlin不支持通配符,即class person(? extends T)
    • 3 声明处型变,即如下所示,只控制一个类中只有返回值才有可能需要校验T泛型,输入是不会出现T泛型这种情况的(同时也有<in T>表示输入)
    abstract class Person<out T> {
      abstract fun getSomeThing(): T
    }
    

    那么这样有什么好处呢?看下面的代码,这在java中不可行,是禁止这样操作的。但是实际上,这样是极为安全的

    fun demo(strs: Person<String>) {
      val objects: Person<Any> = strs 
      // ...
    }
    

    10. 代理

    kotlin当中可以对类和属性进行代理

    • 1 代理类(代理模式)
    interface Person{
      fun run()
    }
    class SuperMan() : Person{
      override fun run() { print("i can fly") }
    }
    class Delegate(person: Person) : Person by person
    fun main(args: Array<String>) {
      val superMan = SuperMan()
      Delegate(b).run() //打印 i can fly"
    }
    
    • 2 代理属性,这是一个超级棒的功能,这里面和很多功能,这里我就介绍两种
    • 代理属性
    class Example {
      var p: String by Delegate()
    }
    class Delegate {
      operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return 'someThing"
        }
    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
        println("value'")
       }
    }
    

    这个这可以在属性获取值和设置值的时候进行代理拦截,这个功能非常强大,例子如下 (代码来源《Kotlin for Android Developer》

    class Preference<T>(val context: Context, val name: String, val default: T, val prefName: String = "default") : ReadWriteProperty<Any?, T> {
        val prefs by lazy { context.getSharedPreferences(prefName, Context.MODE_PRIVATE) }
        override fun getValue(thisRef: Any?, property: KProperty<*>): T {
            return findPreference(name, default)
        }
        override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
            putPreference(name, value)
        }
        private fun <U> findPreference(name: String, default: U): U = with(prefs) {
            val res: Any = when (default) {
                is Long -> getLong(name, default)
                is String -> getString(name, default)
                is Int -> getInt(name, default)
                is Boolean -> getBoolean(name, default)
                is Float -> getFloat(name, default)
                else -> throw IllegalArgumentException("This type can be saved into Preferences")
            }
            res as U
        }
        private fun <U> putPreference(name: String, value: U) = with(prefs.edit()) {
            when (value) {
                is Long -> putLong(name, value)
                is String -> putString(name, value)
                is Int -> putInt(name, value)
                is Boolean -> putBoolean(name, value)
                is Float -> putFloat(name, value)
                else -> throw IllegalArgumentException("This type can be saved into Preferences")
            }.apply()
        }
    }
    

    当使用get方法时,会自动从sharePreference当中取,当给变量设值的时候会自动给把值存储更新到sharePreference当中,可以像使用正常变量一样使用sharePreference,使用方法如下:

    class TestActivity : Activity(){
        var version: Int by Preference(this, "version", 1.0.0)
    }
    
    • 可观察属性(observable properties
      这个的作用,是当一个变量的值改变时,会进行通知,并告知旧值和新值。这个在开发中就非常实用了,比如定位功能,可以判断定位的值是否发生变化,设置了定位信息,从而允许接下来的逻辑。
    var locationCity: String by Delegates.observable("<no location>") {
            prop, old, new ->
            println("$old -> $new")
    }
    

    在开发中我们通常会通过 一个helper工具类来实现对某个对象的属性值来进行控制。比如,当设置的年龄小于15岁的时候不允许更改,在这里使用Delegates.observable()就很方便了,而且逻辑看起来更加清晰明了。
    也可以通过Delegates.vetoable()来控制变量的值是否可以修改(使用方法和 Delegates.observable()一样)

    • 3 属性延迟
      在开发中,我们经常会做一件事件,当一个对象需要使用时才初始化,不使用不初始化。这在kotlin中就很方便实现,当第一次使用时才初始化,随后直接使用初始化后的值(该方法默认是同步的,如果不需要同步可以加上LazyThreadSafetyMode.NONE)
    val user:Student by lazy { 
            Student("girl",18,true)
    }
    

    11. 扩展

    kotlin的扩展是我最喜欢的一个新功能了,之前开发ios的时候就喜欢得不得了,现在在Android中终于可以使用了。在java世界中每个开发者都有一堆自己的工具类,如StringUtilsViewUtilsToastUtils等等。但是这样会有一个问题,比如View的一个工具类,谁知道你的工具类名叫什么?就算知道,也很麻烦。比如设置paddlingLeft的一般套路
    ViewUtils.setPaddingLeft(view,paddingLeft)
    但是通过扩展,就完成清晰明了,和调系统方法没有任务区别,首先编写一个扩展

    fun View.setPaddingLeft(paddingLeftNew: Int){
            setPadding(paddingLeftNew,paddingTop,paddingRight,paddingBottom)
    }
    //调用:
    view.paddingLeft=12
    

    是不是很赞!!!扩展功能非常强大,我们可以把各种工具类进行简化。比如Toast

        fun Any.toast(text: String){
            Toast.makeText(applicationContext,text,Toast.LENGTH_SHORT).show()
        }
    

    当然扩展不止于此,脑洞有多大,扩展就有多强!

    12. 和ButterKnife say goodbye

    每个Android的开发者曾经都为findViewById()而烦恼不已,直到后来ButterKnife的出现。但是ButterKnife是不是就完美了,并不是!当布局复杂的时候,activity里面前面一堆View的变量。那有没有办法去掉这一堆变量,有!

    import kotlinx.android.synthetic.main.activity_kotlin.*
    class Activity{
        btnCommint.text="xxx"
     }
    

    activity_kotlin.xml

     <Button
            android:id="@+id/btnCommit"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            android:text="i am kotlin button"/>
    

    只需要把import布局文件,就可以直接把Id当成变量来使用。轻轻松松和ButterKnife say goodbye

    13. 一些小细节

    • 1Class对象的获取
    View::class.java //在kotlin中获取class对象传递到java代码中
    View::javaClass //在kotlin中获取class对象传递到kotlin代码中
    
    • 2 this的使用
    class KotlinActivity{
      fun isBigNum(num: Int){
    
      }
      inner class Person{
            open fun run(){
                this@Person.run()
                this@KotlinActivity.isBigNum(3)
            }
        }
    }
    
    • 3 SmartCast(类型转换)
      java:
    TextView view=(TextView)findViewById(R.id.tv)
    

    kotlin:

    var view=findViewById(R.id.tv) as TextView
    
    • 4 字符串拼接
      java:
    System.out.println("username:"+user.getUsername()+"--age:"+age)
    

    kotlin:

    print("username:${user.username}--age:$age)
    
    • 5 三目运算符
      kotlin当中没有三目运算符,实现三目运算符的方法:
    var maxValue= if(a>b) a else b
    

    相关文章

      网友评论

        本文标题:kotlin使用初体验

        本文链接:https://www.haomeiwen.com/subject/kithhxtx.html