kotlin使用初体验

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,560评论 4 361
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,104评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,297评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,869评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,275评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,563评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,833评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,543评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,245评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,512评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,011评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,359评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,006评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,062评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,825评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,590评论 2 273
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,501评论 2 268

推荐阅读更多精彩内容