美文网首页
kotlin 入门教程

kotlin 入门教程

作者: allen_li | 来源:发表于2018-09-22 11:02 被阅读0次

    kotlin是 JetBrains 在 2010 年推出的基于 JVM 的新编程语言。开发者称,设计它的目的是避免 Java 语言编程中的一些难题。比如:在 Kotlin 中类型系统控制了空指针引用,可以有效避免 Java 中常见的NullPointException。
    作为一个跨平台的语言,Kotlin 可以工作于任何 Java 的工作环境:服务器端的应用,移动应用(Android版),桌面应用程序。

    Kotlin的优势

    相比于 Java,Kotlin 有着更好的语法结构,安全性和开发工具支持。Kotlin 中没有基础类型,数组是定长的,泛型是安全的,即便运行时也是安全的。此外,该语言支持闭包,还可通过内联进行优化。不过,它不支持检查异常(Checked Exceptions),许多语言设计者认为这是它的瑕疵。不论如何,重要的是 Java 和 Kotlin 之间的互操作性:Kotlin 可以调用 Java,反之亦可。

    变量

    fun main(args: Array<String>) {
        var quantity = 5
        val price: Double = 20.3
        val name: String = "大米"
    
        println("单价:$price")
        println("数量:$quantity")
        println("产品:$name 总计:${quantity * price}")
    }   
    

    in关键字的使用

    //如果存在于区间(1,Y-1),则打印OK
    if (x in 1..y-1) 
      print("OK")
    
    //如果x不存在于array中,则输出Out
    if (x !in 0..array.lastIndex) 
      print("Out")
    
    //打印1到5
    for (x in 1..5) 
      print(x)
    
    //遍历集合(类似于Java中的for(String name : names))
    for (name in names)
      println(name)
    
    //如果names集合中包含text对象则打印yes
    if (text in names)
      print("yes")
    

    when表达式

    类似于 Java 中的 switch,但是 Kotlin 更加智能,可以自动判断参数的类型并转换为响应的匹配值。

    fun cases(obj: Any) { 
      when (obj) {
        1       -> print("第一项")
        "hello" -> print("这个是字符串hello")
        is Long -> print("这是一个Long类型数据")
        !is String -> print("这不是String类型的数据")
        else    -> print("else类似于Java中的default")
      }
    }
    

    智能类型推测

    判断一个对象是否为一个类的实例,可以使用is关键字
    与 Java 中的instanceof关键字类似,但在 Kotlin 中如果已经确定了一个对象的类型,可以在接下来的代码块中直接作为这个确定类型使用。

    fun getStringLength(obj: Any): Int? {
      if (obj is String) {
        // 做过类型判断以后,obj会被系统自动转换为String类型
        return obj.length 
      }
    
      //同时还可以使用!is,来取反
      if (obj !is String){
      }
    
      // 代码块外部的obj仍然是Any类型的引用
      return null
    }
    

    空值检测

    Kotlin 是空指针安全的,也就意味着你不会再看到那恼人的空指针异常。
    例如这句代码 println(files?.size),只会在files不为空时执行。
    以及,你可以这样写

    //当data不为空的时候,执行语句块
    data?.let{
        //... 
    }
    
    //相反的,以下代码当data为空时才会执行
    data?:let{
        //...
    }
    

    函数

    函数的声明

    函数使用关键字fun声明,如下代码创建了一个名为say()的函数,它接受一个String类型的参数,并返回一个String类型的值

    fun say(str: String): String {
        return str
    }
    

    同时,在 Kotlin 中,如果像这种简单的函数,可以简写为

    fun say(str: String): String = str
    

    如果是返回Int类型,那么你甚至连返回类型都可以不写

    fun getIntValue(value: Int) = value
    

    函数的默认参数

    你也可以使用默认参数来实现重载类似的功能

    fun say(str: String = "hello"): String = str
    

    这时候你可以调用say(),来得到默认的字符串 "hello",也可以自己传入参数say("world")来得到传入参数值。
    有时参数非常多的时候,也可以使用多行参数的写法,它们是相同的

    fun say(firstName: String = "Tao",
            lastName: String = "Zhang"){
    }
    

    变参函数

    同 Java 的变长参数一样,Kotlin 也支持变长参数

    //在Java中,我们这么表示一个变长函数
    public boolean hasEmpty(String... strArray){
        for (String str : strArray){
            if ("".equals(str) || str == null)
                return true;
        }
        return false;
    }
    
    //在Kotlin中,使用关键字vararg来表示
    fun hasEmpty(vararg strArray: String?): Boolean{
        for (str in strArray){
            if ("".equals(str) || str == null)
                return true 
        }
        return false
    }
    

    扩展函数

    你可以给父类添加一个方法,这个方法将可以在所有子类中使用。例如,在 Android 开发中,我们常常使用这样的扩展函数:

    fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
        Toast.makeText(this, message, duration).show()
    }
    

    将函数作为参数

    Kotlin 中,可以将一个函数作为参数传递给另一个函数

    fun invokeModule(webview: View?, cmd: String, parameters: String, 
    callback: String,
    callbackFunction: (String, String) -> Unit):String?
    

    上面的代码中,我们传入了一个(String, String) -> Unit的参数

    kotlin类特性

    与 Java 相同,Kotlin 声明类的关键字是class。类声明由类名、类头和类体构成。
    其中类头和类体都是可选的; 如果一个类没有类体,那么花括号也是可以省略的。

    构造函数

    Kotlin 的构造函数可以写在类头中,跟在类名后面,如果有注解还需要加上关键字constructor。这种写法声明的构造函数,我们称之为主构造函数。例如下面我们为Person创建带一个String类型参数的构造函数。

    class Person(private val name: String) {
        fun sayHello() {
            println("hello $name")
        }
    }
    

    在主构造函数中不能有任何代码实现,如果有额外的代码需要在构造方法中执行,你需要放到init代码块中执行。

    class Person(private var name: String) {
        
        init {
            name = "Zhang Tao"
        }
    
        internal fun sayHello() {
            println("hello $name")
        }
    }
    
    

    protected 在 “top-level” 中不可以使用,即不能修饰包级别的方法或者属性等
    private 声明在包含声明的源文件中可见
    internal 声明,在同一模块中的任何地方可见

    次构造函数

    class Person(private var name: String) {
    
        private var description: String? = null
        
        init {
            name = "Zhang Tao"
        }
    
        constructor(name: String, description: String) : this(name) {
            this.description = description
        }
        
        internal fun sayHello() {
            println("hello $name")
        }
    }
    

    枚举类

    enum class Programer {
        JAVA, KOTLIN, C, CPP, ANDROID;
    }
    

    密封类

    sealed class BaseClass {
    
        class Test1 : BaseClass() {
            override fun test() {
                println("Test1实例")
            }
    
        }
        class Test2 : BaseClass() {
            override fun test() {
                println("Test2实例")
            }
        }
        object Test3 : BaseClass() {
            override fun test() {
                println("Test3实例")
            }
        }
        open fun test() {
            println("BaseClass实例")
        }
    }
    

    sealed 修饰的类称为密封类,用来表示受限的类层次结构。例如当一个值为有限集中的 类型、而不能有任何其他类型时。在某种意义上,他们是枚举类的扩展:枚举类型的值集合也是受限的,但每个枚举常量只存在一个实例,而密封类的一个子类可以有可包含状态的多个实例。

    data 数据类

    data 修饰的类称之为数据类。它通常用在我们写的一些 POJO 类上。当 data 修饰后,会自动将所有成员用operator声明,即为这些成员生成类似 Java 的 getter/setter 方法。
    编译器自动从主构造器中的属性导入下面这些成员函数:

    • equals()/hashCode()
    • toString()(形式为User(name=John,age=42))
    • componentN()函数对应着声明的属性顺序
    • copy()函数
    data class User(val name: String, val age: Int)
    

    伴生对象

    由于 Kotlin 没有静态方法。在大多数情况下,官方建议是简单地使用 包级 函数。如果你需要写一个可以无需用一个类的实例来调用、但需要访问类内部的函数(例如,工厂方法或单利),你可以把它写成一个用 companion修饰的对象内的方法。我们称companion修饰的对象为伴生对象。

    class StringUtils {
        companion object {
           fun isEmpty(str: String): Boolean {
                return "" == str
            }
        }
    }
    

    单例类的设计

    class Single private constructor() {
        companion object {
            fun get():Single{
                return Holder.instance
            }
        }
    
        private object Holder {
            val instance = Single()
        }
    }
    

    通过关键字object

    object Single{
    }
    

    委托

    Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托。有点像java的动态代理。

    interface Animal{
        fun bark()
    }
    
    class Dog :Animal {
        override fun bark() {
            println("Wang Wang")
        }
    }
    
    class Cat(animal: Animal) : Animal by animal {
    }
    
    fun main(args: Array<String>) {
       Cat(Dog()).bark()
    }
    

    这样,我们就很成功的让一只猫的叫声用狗去代理掉了,于是上面的main方法执行完后就变成了 Wang Wang。

    属性的委托

    import kotlin.reflect.KProperty
    // 定义包含属性委托的类
    class Example {
        var p: String by Delegate()
    }
    
    // 委托的类
    class Delegate {
        operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
            return "$thisRef, 这里委托了 ${property.name} 属性"
        }
    
        operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {
            println("$thisRef 的 ${property.name} 属性赋值为 $value")
        }
    }
    fun main(args: Array<String>) {
        val e = Example()
        println(e.p)     // 访问该属性,调用 getValue() 函数
    
        e.p = "Runoob"   // 调用 setValue() 函数
        println(e.p)
    }
    

    输出结果:
    Example@433c675d, 这里委托了 p 属性
    Example@433c675d 的 p 属性赋值为 Runoob
    Example@433c675d, 这里委托了 p 属性

    可观察属性 Observable

    @[Kotlin 的标准库中已经内置了很多工厂方法来实现属性的委托。]

    import kotlin.properties.Delegates
    
    class User {
        var name: String by Delegates.observable("初始值") {
            prop, old, new ->
            println("旧值:$old -> 新值:$new")
        }
    }
    
    fun main(args: Array<String>) {
        val user = User()
        user.name = "第一次赋值"
        user.name = "第二次赋值"
    }
    

    执行输出结果:
    旧值:初始值 -> 新值:第一次赋值
    旧值:第一次赋值 -> 新值:第二次赋值

    闭包

    一段程序代码通常由常量、变量和表达式组成,然后使用一对花括号“{}”来表示闭合,并包裹着这些代码,由这对花括号包裹着的代码块就是一个闭包。其实在签名我们也介绍了全局和嵌套函数就是一种特殊的闭包。这里,我们总结了一下,Kotlin语言中有三种闭包形式:全局函数、自嵌套函数、匿名函数体。

    fun main(args: Array<String>) {
        // 执行test闭包的内容
        test
    }
    
    // 定义一个比较测试闭包
    val test = if (5 > 3) {
        println("yes")
    } else {
        println("no")
    }
    

    闭包的用途?

    /**
     * 计数统计
     */
    fun justCount():() -> Unit{
        var count = 0
        return {
            println(count++)
        }
    }
    
    
    fun main(args: Array<String>) {
    
        val count = justCount()
        count()  // 输出结果:0
        count()  // 输出结果:1
        count()  // 输出结果:2
    }
    

    自执行闭包

    自执行闭包就是在定义闭包的同时直接执行闭包,一般用于初始化上下文环境。 例如:

    { x: Int, y: Int ->
        println("${x + y}")
    }(1, 3)
    

    Lambda表达式

    val printMsg = { msg: String -> 
        println(msg) 
    }
    
    fun main(args: Array<String>) {
      printMsg.invoke("hello")
    }
    

    带接收者的with,apply

    var s=with("abc"){
            this.plus("d")
        }
        println(s)
    

    apply始终返回座位实参传递给它的对象

     var r="abc".apply {
            this.plus("d")
        }
    
        println(r)
    

    let表达式一般结合类型安全检查使用

    var x: String? = null
        x?.let {
            println("x=${it}")
        }
    

    集合和泛型

    集合

    所有类声明的泛型尖括号里面如果加入了 out 关键字,则说明这个类的对象是只读的,例如他只有:get()、size()等方法,而没有 set()、remove()等方法。
    相反的,如果没有加 out 关键字,或者换一种记法:如果开头是 MutableXXX 那就是一个跟 Java 中用法一致的集合类。

    open class A
    open class B : A()
    open class C : B()
    
    class TypeArray<in A> {
        
        //in 修饰了 A,表示 A 是可以作为参数的。
        fun getValue(a: A): Int? {
            return a?.hashCode()
        }
        
        //这段代码是非法的,因为A 不能被返回
        fun getA(a: A): A? {
            return a
        }
    }
    

    集合的初始化

    在 Kotlin 中,集合类一般不使用构造方法去初始化,而是使用同一的入口方法,例如初始化一个 MutableList,我们使用的是如下代码:

    val mutableList = mutableListOf(0, 1, 2, 3)
    

    类似的初始化集合对象的方法还有

    //创建一个 List<> 对象
    var list = listOf(0, 1, 2)
    
    //创建一个 Set<> 对象
    val ss = setOf(1, 2, 4)
    

    操作符

    比如说输出wifi密码duowan123
    java实现:

    final String[] a = new String[]{"a", "1", "u", "2", "o", "n", "3", "d", "w"};
    final Integer[] indexs = new Integer[]{7, 10, 2, 4, 11, 8, 0, 5, 1, 3, 15, 6};
    

    常用操作符

    Kotlin 的操作符跟 RxJava 基本一致,不需要额外记忆。

    下标操作类
    • contains —— 判断是否有指定元素
    • elementAt —— 返回对应的元素,越界会抛- IndexOutOfBoundsException
    • firstOrNull —— 返回符合条件的第一个元素,没有 返回null
    • lastOrNull —— 返回符合条件的最后一个元素,没有 返回null
    • indexOf —— 返回指定元素的下标,没有 返回-1
    • singleOrNull —— 返回符合条件的单个元素,如有没有符合或超过一个,返回null
    判断类
    • any —— 判断集合中 是否有满足条件 的元素
    • all —— 判断集合中的元素 是否都满足条件
    • none —— 判断集合中是否 都不满足条件,是则返回true
    • count —— 查询集合中 满足条件 的 元素个数
    • reduce —— 从 第一项到最后一项进行累计
    过滤类
    • filter —— 过滤 掉所有 满足条件 的元素
    • filterNot —— 过滤所有不满足条件的元素
    • filterNotNull —— 过滤NULL
    • take —— 返回前 n 个元素
    转换类
    • map —— 转换成另一个集合(与上面我们实现的 convert 方法作用一样);
    • mapIndexed —— 除了转换成另一个集合,还可以拿到Index(下标);
    • mapNotNull —— 执行转换前过滤掉 为 NULL 的元素
    • flatMap —— 自定义逻辑合并两个集合;
    • groupBy —— 按照某个条件分组,返回Map;
    排序类
    • reversed —— 反序
    • sorted —— 升序
    • sortedBy —— 自定义排序
    • sortedDescending —— 降序

    用于 Android 开发的工具

    Kotlin 团队为 Android 开发提供了一套超越标准语言功能的工具:

    • Kotlin Android 扩展是一个编译器扩展, 可以让你摆脱代码中的 findViewById() 调用,并将其替换为合成的编译器生成的属性。
    • Anko 是一个提供围绕 Android API 的 Kotlin 友好的包装器的库 ,以及一个可以用 Kotlin 代码替换布局 .xml 文件的 DSL。
    • Android KTX 一个 Kotlin 代码的扩展库,比较常用的一些代码块,进行封装,然后在这个基础上,提供更良好的 API,供开发者使用。
      参考链接

    相关文章

      网友评论

          本文标题:kotlin 入门教程

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