• Kotlin入门


    kotlin

    1.变量

    val: 不可重新赋值(等效于final修饰)

    var: 可重新赋值

    如果不是在声明变量的同时就进行赋值需要声明变量类型

    val a : Int
    a = 10
    
    • 1
    • 2

    kotlin去除了基本数据类型,只有对象数据类型

    Int Long Short Float Double Boolean Char Byte
    
    • 1

    2.函数

    一条语句的函数:

    fun 函数名(变量名 : 变量类型 , 变量名 :变量类型):返回值类型 = 函数内容

    fun largerNumber(num1: Int, num2: Int): Int = max(num1, num2)
    
    • 1

    由于这个函数右边通过等号连接,右边max函数必将返回Int值,故kotlin可以推导出左边largerNumber函数的返回值类型为Int,所有函数可以简化为:

    fun largerNumber(num1: Int, num2: Int)  = max(num1, num2)
    
    • 1

    多条语句的函数:

    fun 函数名(变量名 : 变量类型 , 变量名 :变量类型):返回值类型{

    ​ 函数内容

    }

    fun largerNumber(num1: Int, num2: Int): Int {
    	max(num1, num2)
    } 
    
    • 1
    • 2
    • 3

    3.程序的逻辑控制

    if语句

    if语句具有返回值,其他与java一致

    fun largerNumber(num1: Int, num2: Int): Int {
    	var value = 0
    	if (num1 > num2) {
    		value = num1
    	} else {
    		value = num2
    	}
    	return value
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    简化1

    fun largerNumber(num1: Int, num2: Int): Int {
    	var value = if (num1 > num2) {
    		num1
    	} else {
    		num2
    	}
    	return value
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    简化2

    fun largerNumber(num1: Int, num2: Int): Int {
    	return if (num1 > num2) {
    		num1
    	} else {
    		num2
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    简化3

    fun largerNumber(num1: Int, num2: Int): Int = if (num1 > num2) {
    		num1
    	} else {
    		num2
    	}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    简化4

    fun largerNumber(num1: Int, num2: Int)= if (num1 > num2) {
    		num1
    	} else {
    		num2
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    简化5

    fun largerNumber(num1: Int, num2: Int)=if (num1 > num2)  num1 else num2
    
    • 1

    when语句

    ①有返回值

    ②when的判断条件可为任意数据类型

    精确匹配:

    fun getScore(name: String) = when (name) {
        "Tom" -> 86
        "Jim" -> 77
        "Jack" -> 95
        "Lily" -> 100
        else -> 0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    类型匹配:

    fun checkNumber(num: Number) {
        when (num) {
            is Int -> println("number is Int")
            is Double -> println("number is Double")
            else -> println("number not support")
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    when语句不传参:

    fun getScore(name: String) = when {
        name == "Tom" -> 86
        name == "Jim" -> 77
        name == "Jack" -> 95
        name == "Lily" -> 100
    	else -> 0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    无参数when语句使用场景举例:

    ​ 所有名字以Tom开头的人,他的分数都是86分

    ​ name.startsWith(“Tom”) -> 86

    fun getScore(name: String) = when {
        name.startsWith("Tom") -> 86
        name == "Jim" -> 77
        name == "Jack" -> 95
        name == "Lily" -> 100
        else -> 0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    注:kotlin判断字符串或对象是否相等可以直接使用==关键字

    while语句

    ​ 与java一致

    for-in语句

    ​ 补充知识:

    ​ ①闭区间

    val range = 0..10
    
    • 1

    ​ ②左闭右开区间

    var range = 0 until 10
    
    • 1
    fun main() {
        for (i in 0..10) {
            println(i)
        }
    }
    
    fun main() {
        for (i in 0 until 10) {
            println(i)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    注:step关键字:在for-i循环中跳过其中的一些元素

    step 2:相当于i+=2,递增2

    fun main() {
        for (i in 0 until 10 step 2) {
            println(i)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    注:downTo关键字:创建降序的区间

    fun main() {
        for (i in 10 downTo 1) {
            println(i)
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.面向对象

    ​ 4.1类与对象的概念

    ​ 不同于面向过程的语言(比如C 语言),面向对象的语言是可以创建类的。类就是对事物的一种封装,比如说人、汽车、房 屋、书等任何事物,我们都可以将它封装一个类,类名通常是名词。而类中又可以拥有自己的 字段和函数,字段表示该类所拥有的属性,比如说人可以有姓名和年龄,汽车可以有品牌和价 格,这些就属于类中的字段,字段名通常也是名词。而函数则表示该类可以有哪些行为,比如 说人可以吃饭和睡觉,汽车可以驾驶和保养等,函数名通常是动词。 通过这种类的封装,我们就可以在适当的时候创建该类的对象,然后调用对象中的字段和函数 来满足实际编程的需求,这就是面向对象编程最基本的思想。

    class Person {
         var name = ""
         var age = 0
         fun eat() {
             println(name + " is eating. He is " + age + " years old.")
         }
    }
    
    val p = Person()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ​ 4.2继承与构造函数

    ​ 4.2.1继承

    ​ 在Kotlin中任何一个非抽象类默认都是不可以被继承的,相当于Java中给类声明了final 关键字。之所以这么设计,其实和val关键字的原因是差不多的,因为类和变量一样,最好都是 不可变的,而一个类允许被继承的话,它无法预知子类会如何实现,因此可能就会存在一些未 知的风险。Effective Java这本书中明确提到,如果一个类不是专门为继承而设计的,那么就应 该主动将它加上final声明,禁止它可以被继承。。之 所以这里一直在说非抽象类,是因为抽象类本身是无法创建实例的,一定要由子类去继承它才 能创建实例,因此抽象类必须可以被继承才行,要不然就没有意义了。

    //open关键字:让类可被继承
    open class Person {
         ...
    }
    //Student 继承 Person ,将java中的extends关键字替换成了一个冒号
    class Student : Person() {
         var sno = ""
         var grade = 0
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ​ 4.2.2主构造函数与次构造函数

    ​ 主构造函数将会是你最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然 你也可以显式地给它指明参数。主构造函数的特点是没有函数体,直接定义在类名的后面即 可。

    class Student(val sno: String, val grade: Int) : Person() {
    }
    
    • 1
    • 2

    ​ 主构造函数没有函数体,如果我想在主构造函数中编写一些逻辑,该怎么办呢? Kotlin给我们提供了一个init结构体,所有主构造函数中的逻辑都可以写在里面:

    class Student(val sno: String, val grade: Int) : Person() {
         init {
             println("sno is " + sno)
             println("grade is " + grade)
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ​ 子类中的构造函数必须调用父类中的构造函数!!!

    ​ 主构造函数并没有函数体,我们怎样去调用父类的构造函数呢?你可能会说,在init结构体中去调用不就好了。这或许是一种办法,但绝对不是一 种好办法,因为在绝大多数的场景下,我们是不需要编写init结构体的。

    ​ 子类的主构造函数调用父类中的哪个构造函数,在继承的时候通过括号来指定。

    open class Person() {
         ...
    }
    /**
    	Person类后面的一对空括号表示Student类的主构造函数在初始化的时候会调用
        Person类的无参数构造函数
    */
    class Student(val sno: String, val grade: Int) : Person() {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    //Person类现在已经没有无参的构造函数了
    open class Person(val name: String, val age: Int) {
         ...
    }
    
    //给Person类的构造函数传入name和age字段
    /**
    	在Student类的主构造函数中增加name和age这两个字段时,不能再将它们声明成
    val,因为在主构造函数中声明成val或者var的参数将自动成为该类的字段,这就会导致和父
    类中同名的name和age字段造成冲突。因此,这里的name和age参数前面我们不用加任何关键字,让它的作用域仅限定在主构造函数当中即可
    */
    class Student(val sno: String, val grade: Int, name: String, age: Int) :
         Person(name, age) {
         ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ​ 任何一个类只能有一个主构造函数,但是可以有多个次构造函数。次构造函数也可 以用于实例化一个类,这一点和主构造函数没有什么不同,只不过它是有函数体的。其实你几乎是用不到次构造函数的,Kotlin提供了一个给函数设定参数默认值的功能,基本上可 以替代次构造函数的作用。

    ​ Kotlin规定,当一个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造 函数(包括间接调用)。

    class Student(val sno: String, val grade: Int, name: String, age: Int) :
         Person(name, age) {
         //次构造函数是通过constructor关键字来定义的
             
         /**
         	第一个次构造函数接收name和age参数,然后它又通过this关键字调用了主构造函数,并将sno和grade这两个参数赋值成初始值
         */
         constructor(name: String, age: Int) : this("", 0, name, age) {
         }
             
         /**
         	第二个次构造函数不接收任何参数,它通过this关键字调用了我们刚才定义的第一个次构造函数,并将name和age参数也赋值成初始值,第二个次构造函数间接调用了主构造函数
         */  
         constructor() : this("", 0) {
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    /**
    	首先Student类的后面没有显式地定义主构造函数,同时又因为定义了次构造函数,所以现在Student类是没有主构造函数的。那么既然没有主构造函数,继承Person类的时候也就不需要再加上括号了。
    */
    class Student : Person {
        /**
        	由于没有主构造函数,次构造函数只能直接调用父类的构造函数,上述代码也是将this
    关键字换成了super关键字
        */
         constructor(name: String, age: Int) : super(name, age) {
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ​ 4.3接口

    interface Study {
         fun readBooks()
         fun doHomework()
    }
    /**
    	Java中继承使用的关键字是extends,实现接口使用的关键字是implements,而Kotlin中统一使用冒号,中间用逗号进行分隔。
    */
    class Student(name: String, age: Int) : Person(name, age), Study {
         override fun readBooks() {
             println(name + " is reading.")
         }
         override fun doHomework() {
             println(name + " is doing homework.")
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    ​ Kotlin还增加了一个额外的功能:允许对接口中定义的函数进行默认实现。

    ​ 如果接口 中的一个函数拥有了函数体,这个函数体中的内容就是它的默认实现。

    /**
    	现在当一个类去实现Study接口时,只会强制要求实现readBooks()函数,而doHomework()函数则可以自由选择实现或者不实现,不实现时就会自动使用默认的实现逻辑。
    */
    interface Study {
         fun readBooks()
         fun doHomework() {
             println("do homework default implementation.")
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    ​ 修饰符

    修饰符JavaKotlin
    public所有类可见所有类可见(无修饰符时默认)
    private当前类可见当前类可见
    protected当前类、子类、同一包路径下的类可见当前类,子类可见
    default同一包路径下的类可见(无修饰符时默认)
    internal同一模块中的类可见

    ​ 4.4数据类

    ​ 数据类通常需要重写equals()、hashCode()、toString()这几个方法。其中,equals() 方法用于判断两个数据类是否相等。hashCode()方法作为equals()的配套方法,也需要一起 重写,否则会导致HashMap、HashSet等hash相关的系统类无法正常工作。toString()方法 用于提供更清晰的输入日志,否则一个数据类默认打印出来的就是一行内存地址。

    /**
    	当在一个类前面声明了data关键字时,就表明你希望这个类是一个数据类,Kotlin会根据主构造函数中的参数帮你将equals()、hashCode()、toString()等固定且无实际逻辑意义的方法自动生成
    */
    data class Cellphone(val brand: String, val price: Double)
    
    • 1
    • 2
    • 3
    • 4

    ​ 4.5单例类

    ​ 单例模式,这是最常用、最基础的设计模式之一,它可以用于避免创建重 复的对象。比如我们希望某个类在全局最多只能拥有一个实例,这时就可以使用单例模式。

    ​ 首先为了禁止外部创建Singleton的实例,我们需要用private关键 字将Singleton的构造函数私有化,然后给外部提供了一个getInstance()静态方法用于获 取Singleton的实例。在getInstance()方法中,我们判断如果当前缓存的Singleton实例 为null,就创建一个新的实例,否则直接返回缓存的实例即可,这就是单例模式的工作机制。

    //java 创建单例类
    public class Singleton {
         private static Singleton instance;
         private Singleton() {}
         public synchronized static Singleton getInstance() {
             if (instance == null) {
                 instance = new Singleton();
             }
             return instance;
         }
         public void singletonTest() {
         	System.out.println("singletonTest is called.");
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    //kotlin 单例类创建完毕
    
    object Singleton {
        //我们可以直接在这个类中编写需要的函数,比如加入一个singletonTest()函数
        fun singletonTest() {
             println("singletonTest is called.")
         }
    }
    //调用单例类中的函数
    Singleton.singletonTest()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    5.Lambda编程

    ​ 5.1集合的创建与遍历

    ​ 传统意义上的集合主要就是List和Set,再广泛一点的话,像Map这样的键值对数据结构也可 以包含进来。List、Set和Map在Java中都是接口,List的主要实现类是ArrayList和 LinkedList,Set的主要实现类是HashSet,Map的主要实现类是HashMap。

    //java
    val list = ArrayList<String>()
    list.add("Apple")
    list.add("Banana")
    list.add("Orange")
    list.add("Pear")
    list.add("Grape")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    //kotlin ,Kotlin专门提供了一个内置的listOf()函数来简化初始化集合的写法
    
    //listOf()函数创建的是一个不可变的集合。
    //不可变的集合指的就是该集合只能用于读取,我们无法对集合进行添加、修改或删除操作。
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
    //遍历集合
    fun main() {
         val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")
         for (fruit in list) {
             println(fruit)
         }
    }
    
    //mutableListOf()函数可以要创建一个可变的集合.
    fun main() {
         val list = mutableListOf("Apple", "Banana", "Orange", "Pear", "Grape")
         list.add("Watermelon")
         for (fruit in list) {
             println(fruit)
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    ​ 前面我们介绍的都是List集合的用法,实际上Set集合的用法几乎与此一模一样,只是将创建 集合的方式换成了setOf()和mutableSetOf()函数而已。

    ​ Set集合中是不可以存放重复元素的,如果存放了多个相同的元素,只会保留其中一 份,这是和List集合最大的不同之处。

    ​ Map是一种键值对形式的数据结构,因此在用法上和List、 Set集合有较大的不同。传统的Map用法是先创建一个HashMap的实例,然后将一个个键值对数 据添加到Map中。但其实 在Kotlin中并不建议使用put()和get()方法来对Map进行添加和读取数据操作,而是更加推荐 使用一种类似于数组下标的语法结构。

    //JAVA
    val map = HashMap<String, Int>()
    map.put("Apple", 1)
    map.put("Banana", 2)
    map.put("Orange", 3)
    map.put("Pear", 4)
    map.put("Grape", 5)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    //kotlin
    val map = HashMap<String, Int>()
    map["Apple"] = 1
    map["Banana"] = 2
    map["Orange"] = 3
    map["Pear"] = 4
    map["Grape"] = 5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ​ Kotlin毫无疑问地提供了一对mapOf()和 mutableMapOf()函数来继续简化Map的用法。在mapOf()函数中,我们可以直接传入初始化 的键值对组合来完成对Map集合的创建

    //实to并不是关键字,而是一个infix函数
    //infix函数(中缀表达式):Kotlin允许在不使用括号和点号的情况下调用函数,那么这种函数被称为 infix函数。
    val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
    
    //遍历
    fun main() {
         val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
         for ((fruit, number) in map) {
         	println("fruit is " + fruit + ", number is " + number)
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    ​ 5.2集合的函数式API

    ​ Lambda就是一小段可以作 为参数传递的代码。

    ​ 为正常情况下,我们向某个函数传 参时只能传入变量,而借助Lambda却允许传入一小段代码。这里两次使用了“一小段代码”这 种描述,那么到底多少代码才算一小段代码呢?Kotlin对此并没有进行限制,但是通常不建议在 Lambda表达式中编写太长的代码,否则可能会影响代码的可读性。

    ​ Lambda表达式的语法结构:

    ​ {参数名1: 参数类型, 参数名2: 参数类型 -> 函数体}

    ​ maxBy 函数:maxBy函数的工作原理是根据我们传入的条件来遍历集合,从而找到该条件下的最大值。

    //在一个水果集合里面找到单词最长的那个水果
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
    val maxLengthFruit = list.maxBy { it.length }
    println("max length fruit is " + maxLengthFruit)
    
    /**
    	前面使用的函数式API的语法结构看上去好像很特殊,但其实maxBy就是一个普通的函数而已,只不过它接收的是一个Lambda类型的参数,并且会在遍历集合时将每次遍历的值作为参数传递给Lambda表达式。
    */
    val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
    val lambda = { fruit: String -> fruit.length }
    val maxLengthFruit = list.maxBy(lambda)
    
    //简化:不需要专门定义一个lambda变量,而是可以直接将lambda表达式传入maxBy函数当中
    val maxLengthFruit = list.maxBy({ fruit: String -> fruit.length })
    
    //简化:后Kotlin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面
    val maxLengthFruit = list.maxBy() { fruit: String -> fruit.length }
    
    //简化:如果Lambda参数是函数的唯一一个参数的话,还可以将函数的括号省略
    val maxLengthFruit = list.maxBy { fruit: String -> fruit.length }
    
    //简化:由于Kotlin拥有出色的类型推导机制,Lambda表达式中的参数列表其实在大多数情况下不必声明参数类型
    val maxLengthFruit = list.maxBy { fruit -> fruit.length }
    
    //简化:当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替
    val maxLengthFruit = list.maxBy { it.length }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    ​ map函数:它用于将集合中的每个元素都映射成一个另外的 值,映射的规则在Lambda表达式中指定,最终生成一个新的集合

    //比如,这里我们希望让所有的水果名都变成大写模式
    fun main() {
         val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
         val newList = list.map { it.toUpperCase() }
         for (fruit in newList) {
             println(fruit)
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ​ filter函数:filter函数 是用来过滤集合中的数据的,它可以单独使用,也可以配合刚才的map函数一起使用。

    fun main() {
         val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
         val newList = list.filter { it.length <= 5 }
                                           .map { it.toUpperCase() }
         for (fruit in newList) {
             println(fruit)
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ​ any和all函数:

    ​ any函数用于判断集 合中是否至少存在一个元素满足指定条件,all函数用于判断集合中是否所有元素都满足指定条 件。

    fun main() {
         val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
         val anyResult = list.any { it.length <= 5 }
         val allResult = list.all { it.length <= 5 }
         println("anyResult is " + anyResult + ", allResult is " + allResult)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ​ 5.3Java函数式API的使用

    ​ 在Kotlin代码中调用了一个 Java方法,并且该方法接收一个Java单抽象方法接口参数,就可以使用函数式API。Java单抽象方法接口指的是接口中只有一个待实现方法,如果接口中有多个待实现方法,则无法使用函数式API。

    ​ Java函数式API的使用都限定于从Kotlin中调用Java方法

    //最常见的单抽象方法接口--Runnable接口
    public interface Runnable {
     	void run();
    }
    
    new Thread(new Runnable() {
         @Override
         public void run() {
             System.out.println("Thread is running");
         }
    }).start();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    Thread({
     println("Thread is running")
    }).start()//由于Kotlin完全舍弃了new关键字,因此创建匿名类实例的时候就不能再使用new了,而是改用了object关键字
    Thread(object : Runnable {
         override fun run() {
             println("Thread is running")
         }
    }).start()
    
    //简化:因为Runnable类中只有一个待实现方法,即使这里没有显式地重写run()方法,Kotlin也能自动明白Runnable后面的Lambda表达式就是要在run()方法中实现的内容。
    Thread(Runnable {
         println("Thread is running")
    }).start()
    
    //简化:,如果一个Java方法的参数列表中有且仅有一个Java单抽象方法接口参数数,我们还可以将接口名进行省略。
    Thread({
         println("Thread is running")
    }).start()
    
    //简化:当Lambda表达式是方法的最后一个参数时,可以将Lambda表达式移到方法括号的外面。同时,如果Lambda表达式还是方法的唯一一个参数,还可以将方法的括号省略
    Thread {
         println("Thread is running")
    }.start()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    6.空指针检查

    ​ Kotlin利用编译时判空检查的机制几乎杜绝了空指针异常。虽然编译时判空检查的机制有时候会导致代码变得比较难写,但是Kotlin提供了 一系列的辅助工具。

    ​ Kotlin默认所有的参数和变量都不可为空

    ​ Kotlin提供 了另外一套可为空的类型系统,就是在类名的后面加上一个问号

    ​ 比如,Int表 示不可为空的整型,而Int?就表示可为空的整型;

    //本来所有变量和参数都不可为空,也就没有空指针问题;
    //当我们使用可为空的类型系统时,参数就可以为空了,那么参数调用方法就存在空指针问题
    fun doStudy(study: Study?) {
         if (study != null) {
             study.readBooks()
             study.doHomework()
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ​ 判空辅助工具

    ​ ?.操作符:当对象不为空时正常调用相 应的方法,当对象为空时则什么都不做。

    ​ ?:操作符:如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。

    //?.操作符
    fun doStudy(study: Study?) {
         study?.readBooks()
         study?.doHomework()
    }
    //?:操作符
    fun getTextLength(text: String?) = text?.length ?: 0
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    ​ 有的时候我们可能从逻辑上已经将空指针异 常处理了,但是Kotlin的编译器并不知道

    //例子如下:
    var content: String? = "hello"
    fun main() {
         if (content != null) {
             printUpperCase()
         }
    }
    //在main方法中先进行了判空操作再调用printUpperCase方法,逻辑上不存在空指针
    fun printUpperCase() {
         val upperCase = content.toUpperCase()
         println(upperCase)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    非空断言工具:写法是在对象的后面加上!!

    ​ 这是一种有风险的写法,意在告诉Kotlin,我非常确信这里的对象不会为空,所以不用你来帮我 做空指针检查了,如果出现问题,你可以直接抛出空指针异常,后果由我自己承担。

    fun printUpperCase() {
         val upperCase = content!!.toUpperCase()
         println(upperCase)
    }
    
    • 1
    • 2
    • 3
    • 4

    辅助工具——let:

    ​ let既不是操作符,也不是什么关键 字,而是一个函数。这个函 数提供了函数式API的编程接口,并将原始调用对象作为参数传递到 Lambda表 达式中。

    obj.let { obj2 ->
         // 编写具体的业务逻辑
    }
    
    //?.操作符表示对象为空时什么都不做,对象不为空时就调用let函数,而let函数会将study对象本身作为参数传递到Lambda表达式中,此时的study对象肯定不为空了,我们就能放心地调用它的任意方法了。
    fun doStudy(study: Study?) {
         study?.let { stu ->
             stu.readBooks()
             stu.doHomework()
         }
    }
    //简化:?当Lambda表达式的参数列表中只有一个参数时,可以不用声明参数名,直接使用it关键字来代替即可
    fun doStudy(study: Study?) {
     study?.let {
             it.readBooks()
             it.doHomework()
         }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    7.字符串内嵌表达式

    ​ 语法规则

    //Kotlin允许我们在字符串里嵌入${}这种语法结构的表达式,并在运行时使用表达式执行的结果替代这一部分内容。
    "hello, ${obj.name}. nice to meet you!"
    //当表达式中仅有一个变量的时候,还可以将两边的大括号省略
    "hello, $name. nice to meet you!"
    
    • 1
    • 2
    • 3
    • 4

    8.函数参数默认值

    ​ 之前在学习次构造函数用法的时候我就提到过,次构造函数在Kotlin中很少用,因为Kotlin 提供了给函数设定参数默认值的功能,它在很大程度上能够替代次构造函数的作用。

    //给参数设定默认值的方式也很简单,观察如下代码:
    //如果对第一个变量设置参数默认值,当我们调用该方法时,传递的参数会因为类型不匹配而报错。因为kotlin认为我们传入的参数为函数第一位参数。
    fun printParams(num: Int, str: String = "hello") {
         println("num is $num , str is $str")
    }
    
    //Kotlin提供了另外一种神奇的机制,就是可以通过键值对的方式来传参,从而不必像传统写法那样按照参数定义的顺序来传参。
    fun printParams(num: Int = 100, str: String) {
         println("num is $num , str is $str")
    }
    fun main() {
         printParams(str = "world")
    }
    
    //主构造函数
    class Student(val sno: String = "", val grade: Int = 0, name: String = "", age: Int = 0) :
     	Person(name, age) {
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
  • 相关阅读:
    DDT数据驱动
    角速度变化时四元数和旋转矩阵微分方程的证明
    MIT6824 Lab1要求
    Taurus.MVC 微服务框架 入门开发教程:项目集成:1、服务端:注册中心、网关(提供可运行程序下载)。
    Linux操作系统~进程替换,exec系列函数的使用
    网络——IPv6(一)
    Day30 接口测试requests
    UE4/UE5像素流送云推流|程序不稳定、弱网画面糊怎么办?
    如何保护应用?可快速部署的WAF服务器分享
    如何在小程序的wxml中书写函数逻辑,wxs的使用
  • 原文地址:https://blog.csdn.net/weixin_45487120/article/details/127567368