Kotlin开发第一天,快速入门

完整代码Gitee地址:kotlin-demo: 15天Kotlin学习计划

第一天学习内容代码:Chapter1类

目录

知识点1:日志输出

知识点2:变量声明 val、var

知识点3:定义函数、语法糖

知识点4:条件语句 if、where

知识点5:循环语句 for 、代码区间

知识点6:面向对象

知识点7:接口、函数修饰符

知识点8:数据类(数据模型)

知识点9:单例类

知识点10:集合的创建与遍历

List集合用法

Set集合用法

Map集合的用法

知识点11:集合的函数式API

集合的函数式

Java函数式API的使用

 知识点12:空指针检查

知识点13:字符串内嵌表达式

总结


创建一个LearnKotlin.kt类

知识点1:日志输出

fun main() {
    //1、日志打印
    println("日志输出")
}  

点run执行结果

知识点2:变量声明 val、var

fun main() {
    //2、val声明一个不可变变量  对应java的final
    //var声明一个可变变量 好的代码习惯,优先使用val
    val a = 10
    val b = 37
}  

知识点3:定义函数、语法糖

fun main() {
    //3、定义函数
    val a = 10
    val b = 37
    println("methodName = " + methodName(a, b))
    //函数的语法糖写法
    fun methodName(param1: Int, param2: Int) = max(param1, param2)
    println("methodName = " + methodName(a, 20))
}  

//fun(function的简写)是定义函数的关键字
//fun后面的是函数名
//函数名后面紧跟着一对括号,里面可以声明该函数接收什么参数,参数的数量可以是任意多个
//参数括号后面的那部分是可选的,用于声明该函数会返回什么类型的数据
fun methodName(param1: Int, param2: Int): Int {
    //返回两个参数中更大的那个数
    return max(param1, param2)
}

打印日志结果为: 

知识点4:条件语句 if、where

        Kotlin中的条件语句主要有两种实现方式:if和when。

        如果你熟悉Java的话,应该知道Java中的switch语句并不怎么好用。首先,switch只能传入整型或短于整型的变量作为条件,JDK 1.7之后增加了对字符串变量的支持,但如果你的判断逻辑使用的并非是上述几种类型的变量,那么不好意思,switch并不适合你。其次,switch中的每个case条件都要在最后主动加上一个break,否则执行完当前case之后会依次执行下面的case,这一特性曾经导致过无数奇怪的bug,就是因为有人忘记添加break。

        而Kotlin中的when语句不仅解决了上述痛点,还增加了许多更为强大的新特性,有时候它比if语句还要简单好用,现在我们就来学习一下吧

fun main() {
    //4、条件语句 if、where
    conditional()
​​​​​​​}  

//4、条件语句
fun conditional() {
    //用if判断二个函数的较大值
    println("ifMethodName = " + ifMethodName(20, 50))

    //语法糖写法
    fun ifMethodName1(num1: Int, num2: Int) =
        if (num1 > num2) {
            num1
        } else {
            num2
        }

    //语法糖再精简
    fun ifMethodName(num1: Int, num2: Int) = if (num1 > num2) num1 else num2 * 2
    println("ifMethodName = " + ifMethodName(15, 35))

    //用where判断成绩,与Java相比不用写break,且是有返回值的可精简为
    fun whenStudent(name: String) =
        when (name) {
            "Peace" -> 60
            "Jay" -> 72
            "what" -> 80
            else -> 0
        }
    println("ifStudent = " + whenStudent("Jay"))

    //用where判断参数类型
    fun whenType(num: Number) =
        when (num) {
            is Int -> println("is Int")
            is Double -> println("is Double")
            is Long -> println("is Long")
            is Float -> println("is Float")
            is Short -> println("is Short")
            else -> println("else")
        }
    whenType(1234567890987654321)

    //如果我们不在when语句中传入参数的话,还可以这么写
    //举个例子,假设所有名字以Pea开头的人,他的分数都是90分,这种场景如果用带参数的when语句来写就无法实现
    fun whenStu(name: String) = when {
        name.startsWith("Pea") -> 90
        name == "Peace" -> 60
        name == "Jay" -> 72
        name == "what" -> 80
        else -> 0
    }
    println("whenStu = " + whenStu("Peace"))
}

//用if判断二个函数的较大值
fun ifMethodName(num1: Int, num2: Int): Int {
//    var value = 0
//    if (num1 > num2) {
//        value = num1
//    } else{
//        value = num2
//    }
    //if是有返回值的,可以简化为
    return if (num1 > num2) {
        num1
    } else {
        num2
    }
}

 打印日志结果为: 

知识点5:循环语句 for 、代码区间

        Kotlin在for循环方面做了很大幅度的修改,Java中最常用的for-i循环在Kotlin中直接被舍弃了,而Java中另一种for-each循环则被Kotlin进行了大幅度的加强,变成了for-in循环,所以我们只需要学习for-in循环的用法就可以了

        val value = 0..10,这种语法结构看上去挺奇怪的吧?但在Kotlin中,它是完全合法的。上述代码表示创建了一个0到10的区间,并且两端都是闭区间,这意味着0到10这两个端点都是包含在区间中的,用数学的方式表达出来就是[0, 10]。

        Kotlin中可以使用until关键字来创建一个左闭右开的区间,使用step跳过区间内的元素,使用downTo遍历降序区间。

fun main() {
    //5、循环语句 for
    loopInfo()
​​​​​​​}  

//5、循环语句 for
fun loopInfo() {
    //使用for-in遍历区间
    val value = 0..10
    for (i in value) {
        print(" $i")
    }

    //双端闭区间不如单端闭区间好用,相信你一定知道数组的下标都是从0开始的,
    //一个长度为10的数组,它的下标区间范围是0到9,因此左闭右开的区间在程序设计当中更加常用。
    //Kotlin中可以使用until关键字来创建一个左闭右开的区间
    //使用until替代..关键字,就不会打印10
    println(" ")
    val range = 0 until 10
    for (i in range) {
        print(" $i")
    }

    //如果想跳过其中的一些元素,可以使用step关键字
    //上述代码表示在遍历[0, 10)这个区间的时候,每次执行循环都会在区间范围内递增2,
    //相当于for-i循环中i = i + 2的效果
    println(" ")
    for (i in range step 2){
        print(" $i")
    }

    //前面的..和until关键字都要求区间的左端必须小于等于区间的右端,
    //也就是这两种关键字创建的都是一个升序的区间。
    //如果你想创建一个降序的区间,可以使用downTo关键字
    println(" ")
    for (i in 10 downTo 0){
        print(" $i")
    }
    println(" ")
}

 打印日志结果为: 

知识点6:面向对象

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

fun main() {
    //6、面向对象
    objectView()
​​​​​​​}  

//6、面向对象
fun objectView() {
    //拥有了3种方式来对StudentModel类进行实体化,
    //分别是通过不带参数的构造函数、通过带两个参数的构造函数、通过带4个参数的构造函数
    val student1 = StudentModel()
    val student2 = StudentModel("周润发",28)
    val student3 = StudentModel("111222",3,"周润发",28)

    //类中只有次构造函数,没有主构造函数写法
    val student = StudentModel1("333444",6,"刘德华",18)

}
创建StudentModel数据模型
/**
 * Created by: PeaceJay
 * Created date: 2021/10/25
 * Description: 学生信息模型
 */
//继承了PersonModel的姓名与年龄
//sno学号 grade年级 name姓名 age年龄
class StudentModel(sno: String, grade: Int, name: String, age: Int) : PersonModel(name, age) {

    //主构造函数没有函数体,如果我想在主构造函数中编写一些逻辑,该怎么办呢?
    //Kotlin给我们提供了一个init结构体
    init {
        println("sno=$sno, grade=$grade,name=$name,age$age")
    }

    //次构造函数是通过constructor关键字来定义的,通过this关键字调用了主构造函数
    //第一个次构造函数接收name和age参数,然后它又通过this关键字调用了主构造函数,并将sno和grade这两个参数赋值成初始值
    constructor(name: String, age: Int) : this("", 0, name, age) {

    }

    //第二个次构造函数不接收任何参数,它通过this关键字调用了我们刚才定义的第一个次构造函数,
    //并将name和age参数也赋值成初始值,由于第二个次构造函数间接调用了主构造函数,因此这仍然是合法的
    constructor() : this("", 0) {

    }

}
类中只有次构造函数,没有主构造函数写法
//首先Student类的后面没有显式地定义主构造函数,同时又因为定义了次构造函数,
//所以现在Student类是没有主构造函数的。那么既然没有主构造函数,
//继承Person类的时候也就不需要再加上括号了
class StudentModel1 : PersonModel {
    constructor(sno: String, grade: Int,name: String, age: Int) : super(name, age) {
        println("sno=$sno, grade=$grade,name=$name,age$age")
    }
}

 打印日志结果为: 

知识点7:接口、函数修饰符

        Kotlin中的接口部分和Java几乎是完全一致的。接口是用于实现多态编程的重要组成部分。我们都知道,Java是单继承结构的语言,任何一个类最多只能继承一个父类,但是却可以实现任意多个接口,Kotlin也是如此。

        我们可以在接口中定义一系列的抽象行为,然后由具体的类去实现。下面还是通过具体的代码来学习一下,首先创建一个Study接口,并在其中定义几个学习行为。

/**
 * Created by: PeaceJay
 * Created date: 2021/10/25
 * Description: 定义学生接口
 */
interface Study {
    //在Study接口中添加几个学习相关的函数
    fun readBooks()
    fun doHomeWork()

    //我们给doHomework()函数加上了函数体,并且在里面打印了一行日志。
    //如果接口中的一个函数拥有了函数体,这个函数体中的内容就是它的默认实现。
    //现在当一个类去实现Study接口时,只会强制要求实现readBooks()/doHomeWork()函数,
    //而newDoHomeWork()函数则可以自由选择实现或者不实现,不实现时就会自动使用默认的实现逻辑
    fun newDoHomeWork(){
        println("newDoHomeWork")
    }
}

接下来就可以让StudentBean类去实现Study接口了 

//Java中继承使用的关键字是extends,实现接口使用的关键字是implements,
//而Kotlin中统一使用冒号,中间用逗号进行分隔
//必须加var否则无法引用
class StudentBean(var name: String, age: Int) : PersonModel(name, age), Study {

    override fun readBooks() {
        println("$name is readBooks")
    }

    override fun doHomeWork() {
        println("$name is doHomeWork")
    }
}

  现在我们可以在main()函数中编写如下代码来调用这两个接口中的函数

fun main() {
    //7、接口
    interfaceView()
​​​​​​​}  

//7、接口
//接口是用于实现多态编程的重要组成部分。我们都知道,Java是单继承结构的语言,
//任何一个类最多只能继承一个父类,但是却可以实现任意多个接口,Kotlin也是如此
fun interfaceView() {
    val student = StudentBean("刘德华",18)
    doStudy(student)
}

//演示一下多态编程的特性
fun doStudy(student: StudentBean) {
    student.readBooks()
    student.doHomeWork()
    student.newDoHomeWork()
}

 打印日志结果为:  

了解函数的可见性修饰符

        Java中有public、private、protected和default(什么都不写)这4种函数可见性修饰符
Kotlin中也有4种,分别是public、private、protected和internal;
        private:private修饰符在两种语言中的作用是一模一样的,都表示只对当前类内部可见
        public:public修饰符的作用虽然也是一致的,表示对所有类都可见,但是在Kotlin中public修饰符是默认项,而在Java中default才是默认项;
        protected:protected关键字在Java中表示对当前类、子类和同一包路径下的类可见,在Kotlin中则表示只对当前类和子类可见;
        default:Kotlin抛弃了Java中的default可见性(同一包路径下的类可见),引入了一种新的可见性概念,只对同一模块中的类可见,使用的是internal修饰符。比如我们开发了一个模块给别人使用,但是有一些函数只允许在模块内部调用,不想暴露给外部,就可以将这些函数声明成internal;

知识点8:数据类(数据模型)

        数据类通常占据着非常重要的角色,它们用于将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。或许你听说过MVC、MVP、MVVM之类的架构模式,不管是哪一种架构模式,其中的M指的就是数据类。

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

        这里我们新构建一个手机数据类,字段就简单一点,只有品牌和价格这两个字段。如果使用Java来实现这样一个数据类,代码就需要这样写:

/**
 * Created by: PeaceJay
 * Created date: 2021/10/25
 * Description:
 */
//Java版本写法
//数据类通常需要重写get()/set()、equals()、hashCode()、toString()这几个方法
public class Cellphone {
    private String brand;
    private double price;

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }

    public Cellphone(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }

    //toString()方法用于提供更清晰的输入日志,否则一个数据类默认打印出来的就是一行内存地址
    @NotNull
    @Override
    public String toString() {
        return "Cellphone{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                '}';
    }

    //equals()方法用于判断两个数据类是否相等
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Cellphone cellphone = (Cellphone) o;
        return Double.compare(cellphone.price, price) == 0 &&
                Objects.equals(brand, cellphone.brand);
    }

    //hashCode()方法作为equals()的配套方法,也需要一起重写,否则会导致HashMap、HashSet等hash相关的系统类无法正常工作
    @RequiresApi(api = Build.VERSION_CODES.KITKAT)
    @Override
    public int hashCode() {
        return Objects.hash(brand, price);
    }
}

Java的写法就显得比较复杂了,来试试Kotlin:

/**
 * Created by: PeaceJay
 * Created date: 2021/10/25
 * Description:
 */
//你没看错,只需要一行代码就可以实现了!
//神奇的地方就在于data这个关键字,当在一个类前面声明了data关键字时,
//就表明你希望这个类是一个数据类,Kotlin会根据主构造函数中的参数帮你将equals()、hashCode()、toString()等,
//固定且无实际逻辑意义的方法自动生成,从而大大减少了开发的工作量。
data class CellphoneKt(val brand: String, val price: Double) {
    init {
        println("brand=$brand" + "price=$price")
    }
}

        只需要一行代码就可以实现了!就在于data这个关键字,当在一个类前面声明了data关键字时,就表明你希望这个类是一个数据类。

        下面我们来测试一下这个数据类,在main()函数中编写如下代码:

fun main() {
    //8、数据类
    dataView()
​​​​​​​}  

//8、数据类
fun dataView() {
    //在一个规范的系统架构中,数据类通常占据着非常重要的角色,
    //它们用于将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。
    //或许你听说过MVC、MVP、MVVM之类的架构模式,不管是哪一种架构模式,其中的M指的就是数据类

    //下面我们来测试一下这个数据类
    val cellphoneKt1 = CellphoneKt("小米11 12+256",4299.00)
    val cellphoneKt2 = CellphoneKt("红米K40 Pro+ 12+256",3699.00)
    val cellphoneKt3 = CellphoneKt("红米K40 Pro+ 12+256",3699.00)
    println(cellphoneKt1)
    println("cellphoneKt2 equals cellphoneKt3 = " + (cellphoneKt2 == cellphoneKt3))
}

 打印日志结果为:

知识点9:单例类

        单例模式做为最常用、最基础的设计模式之一,它可以用于避免创建重复的对象,来看看Java版本:

/**
 * Created by: PeaceJay
 * Created date: 2021/10/25
 * Description: Java写法单例
 */
public class Singleton {
    //这段代码其实很好理解,首先为了禁止外部创建Singleton的实例,
    //我们需要用private关键字将Singleton的构造函数私有化,
    //然后给外部提供了一个getInstance()静态方法用于获取Singleton的实例。
    //在getInstance()方法中,我们判断如果当前缓存的Singleton实例为null,
    //就创建一个新的实例,否则直接返回缓存的实例即可,这就是单例模式的工作机制

    private static Singleton instance;

    private Singleton() {
    }

    public synchronized static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }

    public void SingletonText(){
        System.out.println("SingletonText");
    }

}

 调用单例类中的方法:

//调用单例类中的方法
Singleton singleton = new Singleton.getInstance();
singleton.SingletonText();

        虽然Java中的单例实现并不复杂,但是Kotlin明显做得更好,它同样是将一些固定的、重复的逻辑实现隐藏了起来,只暴露给我们最简单方便的用法。在Kotlin中创建一个单例类的方式极其简单,只需要将class关键字改成object关键字即可。现在我们尝试创建一个Kotlin版的Singleton单例类:

/**
 * Created by: PeaceJay
 * Created date: 2021/10/25
 * Description:
 */
//在Kotlin中创建一个单例类的方式极其简单,只需要将class关键字改成object关键字即可
object SingletonKt {
    fun singletonText() {
        println("is singletonText")
    }
}

 在main()函数中编写如下代码:

fun main() {
    //9、单例类
    singletonView()
​​​​​​​} 

//9、单例类
fun singletonView() {
    SingletonKt.singletonText()
}
 

 打印日志结果为: 

知识点10:集合的创建与遍历

      传统意义上的集合主要就是List和Set,再广泛一点的话,像Map这样的键值对数据结构也可以包含进来。

List集合用法

        方法一,类似Java的写法:

    val arr = ArrayList<String>()
    arr.add("ABC")
    arr.add("FGH")
    arr.add("HJK")
    //遍历集合
    for (str in arr){
        print("$str ")
    }
    println()
    但是这种初始化集合的方式比较烦琐,为此Kotlin专门提供了一个内置的listOf()函数来简化初始化集合的写法需要注意的是,listOf()函数创建的是一个不可变的集合。
方法二,不可变
    val listOf = listOf("ABC","FGH","HJK","ZXC")
    for (str in listOf){
        print("$str ")
    }
    println()

方法三、可变

创建一个可变的集合,使用 mutableListOf()函数

    val mutableListOf = mutableListOf("ABC","FGH","HJK","ZXC")
    mutableListOf.add("VBN")
    for (str in mutableListOf){
        print("$str ")
    }
    println()

  打印日志结果为: 

Set集合用法

    Set集合的用法几乎与此一模一样,只是将创建集合的方式换成了setOf()和mutableSetOf()函数,
需要注意,Set集合中是不可以存放重复元素的,如果存放了多个相同的元素,只会保留其中一份,这是和List集合最大的不同之处。
    val setOf = setOf("ABC", "ABC", "FGH", "HJK", "ZXC")
    for (str in setOf) {
        print("$str ")
    }
    println()

Map集合的用法

方法一、类似HashMap
    val map = HashMap<String, Int>()
    map.put("小米9", 2000)
    map.put("小米10", 3000)
    map.put("小米11", 4000)
    Kotlin中并不建议使用put()和get()方法来对Map进行添加和读取数据操作,
而是更加推荐使用一种类似于数组下标的语法结构,比如向Map中添加一条数据就可以这么写。
方法二、Kotlin写法
    val newMap = HashMap<String, Int>()
    newMap["小米9"] = 2000
    newMap["小米10"] = 3000
    newMap["小米11"] = 4000
    //读取数据这样写
    val number = newMap["小米9"]
    println("小米10 = $number")
    这仍然不是最简便的写法,因为Kotlin提供了一对mapOf()和mutableMapOf()函数来继续简化Map的用法这里的键值对组合看上去好像是使用to这个关键字来进行关联的,但其实to并不是关键字,而是一个infix函数。
方法三、mapOf、mutableMapOf写法
    val mapOf = mapOf("小米9" to 2000, "小米10" to 3000, "小米11" to 4000)
    val mutableMapOf = mutableMapOf("小米9" to 2000, "小米10" to 3000, "小米11" to 4000)
    //遍历map的数据
    for ((str, pic) in mapOf) {
        println("str = $str pic = $pic")
    }

打印日志结果为: 

​​​

知识点11:集合的函数式API

集合的函数式

        集合的函数式API有很多个,重点学习函数式API的语法结构,也就是Lambda表达式的语法结构。使用集合的函数式API来实现:

Lambda写法 {参数名1:类型,参数名2,类型 -> 函数体}

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

推导Lambda写法,从繁到简

①我们不需要专门定义一个lambda变量,而是可以直接将lambda表达式传入maxBy函数当中

    val listOf = listOf("abc1", "fgh12", "Hjk1234", "zxc123")
    val maxLength2 = listOf2.maxByOrNull { str: String -> str.length }

②Lambda表达式中的参数列表其实在大多数情况下不必声明参数类型

    val maxLength = listOf.maxByOrNull { str -> str.length }

③当Lambda表达式的参数列表中只有一个参数时,不必声明参数名,可以使用it关键字来代替

    val maxLength = listOf.maxByOrNull { it.length }
    println("maxLength = $maxLength")

        集合中的map函数是最常用的一种函数式API,它用于将集合中的每个元素都映射成一个另外的值, 映射的规则在Lambda表达式中指定,最终生成一个新的集合。比如,这里我们希望让所有的参数都变成大写模式:

    val map = listOf.map { it.toUpperCase() }
    for (str in map) {
        println(str)
    }

打印日志结果为:

学习两个比较常用的函数式API——any和all函数。

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

all函数用于判断集合中是否所有元素都满足指定条件 

    //any函数就表示集合中是否存在大于10长度的参数
    val any = listOf.any { it.length > 10 }
    println("至少存在一个元素满足大于10:$any")
    val all = listOf.all { it.length > 3 }
    println("所有元素都满足大于3:$all")

打印日志结果为:

Java函数式API的使用

首先看看Java版本线程

    new Thread(new Runnable(){
        @Override
        public void run() {
            System.out.println("Java Thread");
        }
    }).start();

        这里使用了匿名类的写法,我们创建了一个Runnable接口的匿名类实例,并将它传给了Thread类的构造方法,最后调用Thread类的start()方法执行这个线程。而如果直接将这段代码翻译成Kotlin版本:

    Thread(object : Runnable {
        override fun run() {
            println("Thread Runnable")
        }
    }).start()

        由于Kotlin完全舍弃了new关键字,因此创建匿名类实例的时候就不能再使用new了,而是改用了object关键字。再对以上代码进行精简:

    Thread(Runnable {
        println("Thread Runnable")
    }).start()

        因为Runnable类中只有一个待实现方法,即使这里没有显式地重写run()方法,Kotlin也能自动明白Runnable后面的Lambda表达式就是要在run()方法中实现的内容。还能继续精简:

    Thread {
        println("Thread Runnable")
    }.start()

        另外,如果一个Java方法的参数列表中有且仅有一个Java单抽象方法接口参数,我们还可以将接口名进行省略,这样代码就变得更加精简了

 知识点12:空指针检查

        Android系统上崩溃率最高的异常类型就是空指针异常(NullPointerException),主要是因为空指针是一种不受编程语言检查的运行时异常,只能由程序员主动通过逻辑判断来避免,但即使是最出色的程序员,也不可能将所有潜在的空指针异常全部考虑到。

        然而,Kotlin却非常科学地解决了这个问题,它利用编译时判空检查的机制几乎杜绝了空指针异常。虽然编译时判空检查的机制有时候会导致代码变得比较难写,但是不用担心,Kotlin提供了一系列的辅助工具,让我们能轻松地处理各种判空情况,直接写doStudy(null)会报错,那么允许参数为空就需要写成student: StudentBean?,如下所示:


fun nullView() {
    /**允许student参数为空,Kotlin会进行空指针检查,如下写法会抛出异常,在参数后面增加?,可以null检查*/
    doStudy(null)
}

fun doStudy(student: StudentBean?) {
    //代码也可简化为student?.readBooks()
    student?.readBooks()
    student?.doHomeWork()
    student?.newDoHomeWork()
}

        接下来我们通过一个具体的例子来结合使用?.和?:这两个操作符,从而让你加深对它们的理解。比如现在我们要编写一个函数用来获得一段文本的长度,使用传统的写法就可以这样写:

//12、空指针检查
fun nullView() {
    println("长度 = " + getTextLength("200"))
    println("长度 = " + getNewTextLength("2000"))
}


//编写一个函数用来获得一段文本的长度,允许为null
fun getTextLength(text: String?): Int? {
    return text?.length
}

//判空辅助工具?:
//获得一段文本的长度,允许为null简化后
//这里我们将?.和?:操作符结合到了一起使用,首先由于text是可能为空的,
//因此我们在调用它的length字段时需要使用?.操作符,而当text为空时,
//text?.length会返回一个null值,这个时候我们再借助?:操作符让它返回0
fun getNewTextLength(text: String?) = text?.length ?: 0


这段代码看上去也并不复杂,但是我们却可以借助操作符?:让它变得更加简单。

 打印日志结果为:

        不过Kotlin的空指针检查机制也并非总是那么智能,有的时候我们可能从逻辑上已经将空指针异常处理了,但是Kotlin的编译器并不知道,这个时候它还是会编译失败。观察如下的代码示例:

 如果我们想要强行通过编译,可以使用非空断言工具!!:


var content: String? = "null"
fun nullView() {
    //这段代码一定是无法运行的。因为printUpperCase()函数并不知道外部已经对content变量进行了非空检查,
    if (content != null) {
        //在调用toUpperCase()方法时,还认为这里存在空指针风险,从而无法编译通过
        //content.toUpperCase()
        //如果我们想要强行通过编译,可以使用非空断言工具
        content!!.toUpperCase()
    }

}

知识点13:字符串内嵌表达式

Kotlin从一开始就支持了字符串内嵌表达式的功能,弥补了Java在这一点上的遗憾。

Kotlin允许我们在字符串里嵌入${}这种语法结构的表达式。

    val a = "and"
    //当表达式中仅有一个变量的时候,还可以将两边的大括号省略
    val str = "Get up $a breathe every day"
    println(str)
    val map = HashMap<String, Int>()
    map["小米11"] = 4000
    //当表达式中不止一个变量的时候,需要增加大括号
    val mapName = "手机售价${map["小米11"]}"
    println(mapName)

打印日志结果为:

         函数的参数默认值,一个非常有用的小技巧,给函数设定参数默认值, 我们可以在定义函数的时候给任意参数设定一个默认值, 这样当调用此函数时就不会强制要求调用方为此参数传值,在没有传值的情况下会自动使用参数的默认值:

//13、字符串内嵌表达式
fun embedStrView() {
    strTest("strTest")
    //类型不匹配
    //strTest(100)
    /**Kotlin提供了另外一种神奇的机制,就是可以通过键值对的方式来传参,从而不必像传统写法那样按照参数定义的顺序来传参*/
    strTest(int = 200, str = "strTest")
}

fun strTest(str: String = "", int: Int = 10) {
    println("str=$str int=$int")
}

这时如果想让int参数使用默认值该怎么办呢?模仿刚才的写法肯定是行不通的,因为编译器会认为我们想把字符串赋值给第一个num参数,从而报类型不匹配的错误。

Kotlin提供了另外一种神奇的机制,就是可以通过键值对的方式来传参,从而不必像传统写法那样按照参数定义的顺序来传参:

总结

在这一天里,我们全面学习了Kotlin编程中最主要的知识点,包括变量和函数、逻辑控制语句、面向对象编程、Lambda编程、空指针检查机制,等等,以上知识点掌握,有足够的实力使用Kotlin来学习Android程序开发了。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

peacejay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值