kotlin上手半年真实体验,纯干货!妥妥玩转语法

100 阅读4分钟

4月份,因负责的项目变更,开始了纯kotlin编码。以下是我在开发过程中总结的kotlin体会,希望对上手时间不长的朋友有帮助。

变量声明

  1. val

不可变变量声明

val people:People = People()
val age:Int= 29

people=People() //提示错误
age=28  //提示错误
  1. var

可变变量声明,需要设置初始值

var people:People = People()
people=People() // 正常
  1. lateinit

可变变量延迟初始化

private lateinit var name :String

空安全

(String )非空

var name:String="wenjie"
fun setNamne(nameTp:String){
    name=nameTp
}


/*调用*/

setName(null) //提示错误,非空不能接受为null的值,如果是运行期间的赋值,那将会发生崩溃。

(String)可为空

var name:String?="wenjie"


/*调用*/

name=null                //√

val length=name.length() //提示错误  可空声明不能直接调用

val length=name?.length() //√

操作域语法糖

let

常用来调用可为空对象时使用

var id:String?=""

/*idb不为空,则执行逻辑 一级内容设置*/
id?.let{
    doSomething(it)
}

/**若let嵌套使用,则往往无法分辨it指代的是谁,可以通过lambda表示式区分*/
id?.let{
    doSomething(it)
    xName?.let{ xNameIt- >
        doSomething2(xNameIt)
    }
}

apply

常用来给一个对象设置很多属性 ,一般用于配置

val people :People= People()
people.apply{
    setName("name")
    setAge(29)
    setLike("read")
}

run

可与let作用相同使用,但其{}lambda表达式内返回的是 语句执行的结果

val str:String? = "hello world"
str?.run{
    toUpperCase()
}.run{
    println(this)
}

class

声明类,普通类无法被继承

/*普通类*/
class Block{}

data class

数据类,专门用于生成自定义数据的类,省去了get set方法的代码;也更单一职责,不易与其他类混淆

/**声明一个数据类.
1.()内声明的属性均具有getter setter方法。
2.可设置默认值,可不设置
3.默认值是public访问权限的
*/
data class Datas(var name1:String="",
                 var age1:Int=9,
                 var people:People?,
                 var like:Like)

object

单例类声明

1.类中所有方法均为静态的,通过类名直接可以访问

2.声明静态变量不需要companion object {}域的声明

object class ActivityManager{
    
}

interface

声明接口

interface IHappy{
    /*默认为public 权限,子类需要实现*/
    fun howToHappy()
	/*有{}实现的,子类不需要强制实现*/
    fun whyHappy(){}
	/*带返回值,带参数的声明*/
    fun hasHapppyPower(userName:String):Boolean
}

open

类的修饰符,放置在class 前;代表此类打开了被继承的权限;可以有子类继承

open class View{}

inner class

内部类对应java中的非静态内部类。

class Abc{
    inner class Bcd{
        fun testInnerSome(){}
    }
}

fun test(){
    Abc().Bcd().testInnerSome()
}

表达式

if else

  • 普通用法与java一致

    if(a){

    }else{

    }

  • 赋值用法,可直接赋值给变量

    val result= if(x){ 20 }else{ 30 }

    /常见多元判断条件/ if(a){

    }else if(){

    }else{

    }

when

  • 类似于switch case,是一种lambda风格语法

    when(x){ 1-> {print("x1")} 2-> {print("x2")} else ->{print("else")} }

  • 枚举和密封类可以不需要else,因为编译器检测范围确定

    enum class Bit{ ZERO, ONE }

    val result=when(getBit()){ Bit.ZERO -> 0 Bit.ONE -> 1 }

  • 可以使用任意表达式作为分支条件

    when(x){ x+b==2->{} else->{} }

  • 取代if-else if 链(所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支)

    when{ x.isOk()->{} x.isHappy()->{} else ->{} }

for

for(item in collList){
    
}


/*数字区间表达式*/
for(i in 1..9){
    
}

/*对于数组的遍历*/
val array = arrayOf("1","2","3")

for( i in array.indices){
     println(array[i])   
}

while

与java一致,不太常用

while(x>0){
    
}


do{
    y--
}while(y==0)

集合

  • list 声明

    /可变的/ val numbers = mutableListOf("one","two","three") val numbers = arrayListOf("one","two","three")

    /不可变/ val numbers = listOf("one","two","three")

  • map 声明

    /不可变/ val maps = mapOf<Int,String>(2 to "b",1 to "a",3 to "c")

    /可变/ val mutableMaps= mutableMapOf<Int,String>(2 to "b",1 to "c",3 to "x")

    hashMapOf linkedMapOf

    /遍历/ maps.forEach{(k,v)-> println("xx-v") }

高阶函数编程

高阶函数是将函数用做参数或者返回值的函数

fun <T,R> Collection<T>.fold(
    initial:R,
    combine:(acc:R,nextElement:T)->R
):R{
    var accumulator:R = initial
    for(element:T in this){
        accumulator = combine(accumulator,element)
    }
    return accumulator
}

扩展函数

  • 扩展函数可以对现有的类进行拓展一些很方便的方法,比如工具类,view操作类,可以减少模版代码

  • 扩展并不能真正的修改某个类的方法,是一种包装延伸

    fun String.hasHappyElement():Boolean{ ... }

    fun View.hide(){ visibility=View.GONE }

  • 扩展函数可以重载,但不能重写某个类的方法

    /重载/ fun main() {

    class Example {
        fun printFunctionType() { println("Class method") }
    }
    
    fun Example.printFunctionType(i: Int) { println("Extension function #$i") }
    
    Example().printFunctionType(1)
    //结果输出Extension fun...
    

    }

    /触发重写或本意是重写/ fun main() {

    class Example {
        fun printFunctionType() { println("Class method") }
    }
    
    fun Example.printFunctionType() { println("Extension function") }
    
    Example().printFunctionType()
    //结果返回 Class method
    

    }

  • 扩展属性(因为扩展属性不能将属性加入到类中,因此无法有初始化器。只能由显示提供的getter/setter提供)

    val House.number :Int =1 //错误,不能拥有初始化器

    val List.lastIndex:Int get()=size-1 //正确

  • 伴生对象的扩展

    class MyClass { companion object { } // 将被称为 "Companion" }

    fun MyClass.Companion.printCompanion() { println("companion") }

    fun main() { MyClass.printCompanion() }

  • 扩展的作用域

一般情况在顶层定义扩展,无需导入。

也可以定义在外部,使用时导入包即可。

值比较

==:结构相等性操作符

  • 该操作符用于检查两个变量或对象的值或内容是否相等。
  • 通常用于比较基本数据类型,因为它会比较传递的对象或变量的值。
  • 例如,4 == 4 返回true,因为它比较的是值。

===:引用相等性操作符

  • 该操作符用于检查两个变量或对象是否指向相同的内存位置。
  • 简单来说,两个变量必须指向同一个对象。
  • 如果对基本类型(如Int、Double)使用===,则===将转换为==,即它将比较内容而不是检查相同的引用。

字符串拼接

+操作符、plus() :或多创建一个字符串对象

val a="33
val b="dd"
val c="$a"+"$b"+"happy

字符串模版

val a="33
val b="dd"
val c="$a $b"

StringBuilder

val sb = StringBuilder()
sb.append("Hello")
sb.append(" ")
sb.append("World")
val c = sb.toString()
println(c)  

类型检测与转换

  • is操作符 !is

    if(obj is String){ print(obj.length) }

    if(obj !is String){ print("Not is String") }

inline

  • 内联函数可以在编译后减少函数体{}Function0创建的内存开销

    /不使用内联/ fun main(args:Array){ foo{ System.out.print("hello") } }

    fun foo(block:()->Unit){ block() }

    //实际调用时(java): foo((Function0)null.INSTANCE) block.invoke()

    /使用内联后/ String var1="hello" System.out.print(var1) //没有额外创建对象,只是对闭包内的函数体进行了粘贴

  • 一般非高阶函数不需要内联优化,反而会使字节码变的复杂

  • 避免对具有大量函数体的函数进行内联,会导致更多的字节码

  • 一旦内联后,不能获取闭包的私有成员