《Android》Chap.2 入门Kotlin

Hello Kotlin

在这里插入图片描述
kotlin每一行代码的结尾不需要加分号。

编程之本

变量

关键词

语法规则
代码原词用法
valvalue声明一个不可变的变量,在初始赋值后不能在被重新赋值
varvariable声明一个可变的变量,在初始赋值后仍能在被重新赋值
代码实践
package com.example.hellokt

fun main(){
    val a = 10
    println("a = " + a)
}

在这里插入图片描述

数据类型

语法规则

Kotlin中使用了对象数据类型,是一个拥有自己的方法和继承结构的类。

数据类型说明
Int整型
Long长整型
Short短整型
Float单精度浮点型
Double双精度浮点型
Boolean布尔型
Char字符型
Byte字节型
代码实践
声明不可变整型变量

在这里插入图片描述
报错原因:声明a的关键字为val变量,后续就不能再赋值。

声明可变整型变量
package com.example.hellokt

fun main(){
    var a: Int = 10
    a = a * 10
    println("a = " + a)
}

在这里插入图片描述

函数

语法规则

//fun 函数名(参数1名: 参数类型, 参数2名: 参数类型): 函数返回值类型{}
fun methodName(param1: Int, param2: Int): Int{
    return 0
}
  • 参数的数量可以是任意多个
  • 如果函数不需要返回任何数据,可以不写返回值类型

代码实践

返回较大数
package com.example.hellokt

import kotlin.math.max


fun main(){
    val a = 37
    val b = 40
    val value = largerNumber(a,b)
    println("larger number is " + value)
}

fun largerNumber(num1: Int, num2: Int): Int{
    return max(num1,num2)
}

在这里插入图片描述

代码简化

如果函数中只有一行代码时,可以不必编写函数体,中间用等号链接就好

fun largerNumber1(num1: Int,num2: Int): Int = max(num1,num2)

Kotlin有出色的推导机制,因为max()返回的是Int值,所以通过等号与它相连的largerNumber2()函数返回值也是Int值,可以省略返回值类型

fun largerNumber2(num1: Int,num2: Int) = max(num1,num2)

程序的逻辑控制

if条件语句

语法规则

Kotlin中的if相较与其他的编程语言有一个额外的功能:它可以有返回值!
返回值就是if语句每一个条件中最后一行代码的返回值。

代码实践

返回较大数(基础版)
fun largerNumber(num1: Int, num2: Int): Int{
    var value = 0
    if (num1 > num2){
        value = num1
    } else {
        value = num2
    }
    return value
}
返回较大数(简化版)
fun largerNumber1(num1: Int,num2: Int): Int {
    var value = if (num1 > num2){
        num1
    } else {
        num2
    }
    return value
}
返回较大数(精简版)
fun largerNumber2(num1: Int,num2: Int): Int {
    return if (num1 > num2){
        num1
    } else {
        num2
    }
}
返回较大数(进一步精简版)
fun largerNumber3(num1: Int,num2: Int) = if (num1 > num2){
    num1
} else {
    num2
}
返回较大数(终极精简版)
fun largerNumber4(num1: Int,num2: Int) = if (num1 > num2) num1 else num2

when条件语句

精确匹配

语法规则

在需要很多判断条件时,可以使用when语句
伪代码:

when (参数){
	匹配值 -> {执行逻辑}
	匹配值 -> {执行逻辑}
	匹配值 -> {执行逻辑}
	匹配值 -> {执行逻辑}
	else -> {执行逻辑}
}

执行逻辑只有一行时,花括号可以省略

代码实践

在这里插入图片描述

类型匹配

语法规则

通过when语句和is关键词,对变量的类型进行判断
伪代码:

when (参数){
	is 匹配类型 -> {执行逻辑}
	is 匹配类型 -> {执行逻辑}
	is 匹配类型 -> {执行逻辑}
	else -> {执行逻辑}
}

执行逻辑只有一行时,花括号可以省略

代码实践

在这里插入图片描述

不带参用法

代码实践

在这里插入图片描述

字符串判断(补充)

语法规则

判断字符串和对象是否相等可以直接使用变量 == 关键字
字符串.startsWith(“xxx”),可以匹配所有以xxx开头的字符串

代码实践

在这里插入图片描述

循环语句(for循环)

Kotlin中的 while语法与java编程语言基本相同

双闭端区间

语法规则

val range = 0..10创建一个双端闭区间,表示为 [ 0 , 10 ] [0,10] [0,10]
其中..为关键字,在..左右两边指定区间的左右端点

代码实践

在这里插入图片描述

左闭右开区间

语法规则

val range = 0 until 10创建一个左闭右开区间,表示为 [ 0 , 10 ) [0,10) [0,10)
其中until为关键字,在until左右两边指定区间的左右端点
for循环中你还可以通过step关键字,实现跳过一些元素的效果

代码实践

在这里插入图片描述
相当于for(int i = 0;i < 10;i += 2)

降序区间

语法规则

关键字a downTo b,创建一个范围为 [ a , b ] [a,b] [a,b]的降序区间

代码实践

在这里插入图片描述
for-in循环不仅可以用来遍历区间还可以遍历集合

面向对象编程

类和对象

语法规则

创建一个Person类:

class Person {
    var name = ""
    var age = 0

    fun eat() {
        println(name + " is eating. He is " + age + " years old.")
    }
}

main函数中进行实例化

fun main(){
    val p =Person()
    p.name = "Jack"
    p.age = 19
    p.eat()
}

p就是Person类的一个实例,也可以成为一个对象

代码实践

在这里插入图片描述

继承

语法规则

父类:在类的前面加上open关键字,则表示该类可以被继承

open class Person{...}

子类:继承的关键字为:

class Student : Person(){...}

代码实践

构造函数

主构造函数

语法规则

每一个类默认会有一个不带参数的主构造参数,当然也可以指明参数。
主构造参数的特点是没有函数体,直接定义在类名的后面即可。

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

主构造函数中的逻辑语句写在init结构体

代码实践

在这里插入图片描述

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

当父类中的构造函数有很多时可以在继承时通过父类后面的括号来指定
错误示范:
在这里插入图片描述
想要解决这个错误可以在Student类的主构造函数中加上着它父类需要的参数,在将参数传递给父类
在这里插入图片描述
注意:在Student类的主构造函数中增加name和age这两个字段时, 不能再将它们声明成val,因为在主构造函数中声明成val或者var的参数 将⾃动成为该类的字段,这就会导致和⽗类中同名的name和age字段造成 冲突。因此,这⾥的name和age参数前⾯我们不⽤加任何关键字,让它的作⽤域仅限定在主构造函数当中即可。
在这里插入图片描述

次构造函数

任何⼀个类只能有⼀个主构造函数,但是可以有多个次构造函数。次构造函数也可以⽤于实例化⼀个类,这⼀点和主构造函数没有什么 不同,只不过它是有函数体的。
Kotlin规定,当⼀个类既有主构造函数又有次构造函数时,所有的次构造函数都必须调⽤主构造函数(包括间接调⽤)。

语法规则

关键字constructor

class Student(val sno: String,val grade: Int, name: String, age: Int) : 
        Person(name,age){    
    constructor(name: String, age: Int) : this("",0,name,age){}
    constructor() : this("",0){}
}
  • 第⼀个次构造函数接收nameage参数,然后它⼜通过this关键字调用了主构造函数,并将snograde这两个参数赋值成初始值;
  • 第⼆个次构造函数不接收任何参数,它通过this关键字调用了第⼀个次构造函数,并将nameage参数也赋值成初始值。
  • 第⼆个次构造函数属于间接调⽤主构造函数。

经过上面的代码现在有三种方式对Student类进行实例化

val stu1 = Student()
val stu2 = Student("Jack", 19)
val stu3 = Student("a123", 5, "Jack", 19)
类中只有次构造函数时

在这里插入图片描述
因为Student类没有主构造函数,继承Person类的时候也就不需要再加上括号了

接口

语法规则

关键字含义
interface定义接口
override实现接口中的函数
:使用接口
,分割继承的父类和使用的接口

代码实现

在这里插入图片描述

  • Study接⼝中定义了readBooks()doHomework()这两个待实现函数, 因此Student类必须实现这两个函数。
  • doStudy()函数接收⼀个Study类型的参数, 由于Student类实现了Study接⼝,因此Student类的实例是可以传递给doStudy()函数的,接下来调用Study接⼝readBooks()doHomework()函数,这种就叫作⾯向接⼝编程,也可以称为多态

对接口中的函数进行默认实现

在这里插入图片描述

可见性修饰符

修饰符Kotlin中的含义Java中的含义
public所有类可见(默认)所有类可见
private当前类可见当前类可见
protected当前类、子类可见当前类、子类、同一包路径下的类可见
default同一包路径下的类可见(默认)
internal同一模块中的类可见

数据类

在⼀个规范的系统架构中,数据类通常占据着非常重要的角色,用于将服务器端或数据库中的数据映射到内存中,为编程逻辑提供数据模型的支持。

通常需要重写的方法

方法名用途
equals()判断两个数据类是否相等
hashCode()equals()的配套⽅法,也需要⼀起重写,否则会导致HashMapHashSethash相关的系统类⽆法正常⼯作
toString提供更清晰的输入日志,否则⼀个数据类默认打印出来的就是一行内存地址。

代码对比

构建⼀个手机数据类,只有品牌和价格两个字段。

Java代码
public class CellphoneJ {
    String brand;
    double price;
    public CellphoneJ(String brand, double price) {
        this.brand = brand;
        this.price = price;
    }
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof CellphoneJ) {
            CellphoneJ other = (CellphoneJ) obj;
            return other.brand.equals(brand) && other.price == price;
        }
        return false;
    }
    @Override
    public int hashCode() {
        return brand.hashCode() + (int) price;
    }
    @Override
    public String toString() {
        return "CellphoneJ(brand=" + brand + ", price=" + price + ")";
    }
}
Kotlin代码
data class Cellphone(val brand: String, val price: Double)

java中大段的代码用kotlin实现只需要一行
当在⼀个类前面声明了data关键字时,就表明这个类是⼀个数据类,Kotlin会根据主构造函数中的参数将equals()hashCode()toString()等固定且无实际逻辑意义的方法自动生成,从而大大减少了开发的工作量。

代码实现

在这里插入图片描述
补充:尝试了一下书中说的删除data关键字,得到了截然不同的结果
在这里插入图片描述

单例类

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

代码对比

Java代码
public class SingletonJ {
    private static SingletonJ instance;
    private SingletonJ() {}
    public synchronized static SingletonJ getInstance() {
        if (instance == null) {
            instance = new SingletonJ();
        }
        return instance;
    }
    public void singletonJTest() {
        System.out.println("singletonJTest is called.");
    }
}

在调用上述代码时:

SingletonJ singletonJ = SingletonJ.getInstance();
singletonJ.singletonTest();
Kotlin代码
object Singleton {
}

此时Singleton就已经是⼀个单例类了
可以在其中直接编写需要的函数:

object Singleton {
    fun singletonTest() {
        println("singletonTest is called.")
    }
}

调用方法:

Singleton.singletonTest()

Lambda编程

集合的创建与遍历

List

初始化方法1(基础)
val list = ArrayList<String>()
list.add("Apple")
list.add("Banana")
list.add("Orange")
list.add("Pear")
list.add("Grape")
初始化方法2(不可变)
val list = listOf("Apple","Banana","Orange","Pear","Grape")
初始化方法3(可变)
val list = mutableListOf("Apple","Banana","Orange","Pear","Grape")
list.add("Watermelon")
遍历集合

在这里插入图片描述

Set

Set中不可以存放重复元素,如果存放了多个相同的元素,只会保留其中的一份。
用法与List基本相同,只是创建集合的方式变成了setOf()mutableSetOf()

Map

Map是⼀种键值对形式的数据结构

初始化方法1(传统)

传统的Map⽤法是先创建⼀个HashMap的实例,然后将⼀个个键值对数据添加到Map中。

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)
初始化方法2(“数组下标”)
val map = HashMap<String, Int>()
map["Apple"] = 1
map["Banana"] = 2
map["Orange"] = 3
map["Pear"] = 4
map["Grape"] = 5

在读取时:

val num = map["Apple"]
初始化方法3(不可变)
val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
初始化方法4(可变)
val map = mutableMapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4)
遍历集合

在这里插入图片描述

集合的函数式API

不知是因为版本问题还是其他什么原因,这一小节的代码我在尝试的时候会报错,且暂时没有找到解决的方法,所以决定暂时跳过,预计于2021.11.21前解决问题并将这一段补上。不好意思
在这里插入图片描述

暂时在网上搜寻无果,如果有大佬看到这篇文章,且能根据报错信息提出修改建议,本人感激不尽

Java函数式API的使用

API的英文全程为:Application Programming Interface,中文译为应用程序编程接口。所以,API就是指接口。
所谓函数式接口,就是指那些只定义了一个待实现的抽象函数的接口。

Kotlin中调⽤Java方法时也可以使⽤函数式API,只不过这是有⼀定条件限制的。具体来讲,如果我们在Kotlin代码中调用了⼀个Java方法,并且该方法接收⼀个Java单抽象方法接口参数,就可以使用函数式API。Java单抽象方法接口指的是接口中只有⼀个待实现方法,如果接口中有多个待实现方法,则无法使用函数式API。

示例1:Runnable接口

使用匿名类的写法,创建⼀个Runnable接口的匿名类实例,并将它传给了Thread类的构造方法,最后调用Thread类start()方法执行这个线程。

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单抽象方法接口参数,我们还可以将接口名进行省略。当Lambda表达式是方法的最后⼀个参数时,可以将Lambda表达式移到方法括号的外面。同时,如果Lambda表达式还是方法的唯⼀⼀个参数,还可以将方法的括号省略,最终简化结果如下:

Thread { println("Thread is running") }.start()

三段代码最终可以得到同样的运行结果:
在这里插入图片描述

示例1:OnClickListener接口

Kotlin代码

button.setOnClickListener{
}

空指针检查

空指针是⼀种不受编程语⾔检查的运行时异常,只能由程序员主动通过逻辑判断来避免,但即使是最出色的程序员,也不可能将所有潜在的空指针异常全部考虑到。

可空类型系统

Kotlin利用编译时判空检查的机制几乎杜绝了空指针异常,将空指针异常的检查提前到了编译时期。
在这里插入图片描述

语法规则

可为空的类型系统就是在类名的后面加上⼀个问号?
Int表示不可为空的整型,⽽Int?就表示可为空的整型;
String表示不可为空的字符串,⽽String?就表示可为空的字符串。

代码实践

在这里插入图片描述
由于我们将参数改成了可为空的Study?类型,此时调用参数的readBooks()doHomework()方法都可能造成空指针异常,因此Kotlin在这种情况下不允许编译通过。
修改方法
在这里插入图片描述

判空辅助工具

?.

语法规则

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

代码实践

在这里插入图片描述

?:

语法规则

操作符:?:
操作符的左右两边都接收⼀个表达式,如果左边表达式的结果不为空就返回左边表达式的结果,否则就返回右边表达式的结果。

代码实践
val c = a ?: b

相当于

val c = if (a ! = null) {
	a
} else {
	b
}

综合实践

在这里插入图片描述
改写后:

fun getTextLength1(text: String?) = text?.length ?: 0

非空断言工具

在这里插入图片描述
报错原因printUpperCase()函数并不知道外部已经对content变量进行了非空检查,在调用toUpperCase()方法时,还认为这里存在空指针风险,从而无法编译通过。
如果想要强行通过编译,可以使用非空断言工具,对象的后面加上!!
在这里插入图片描述
注意:使用这个工具要慎重,一定要确信它不会存在空指针异常!!!

let

语法规则

let是⼀个函数,提供了函数式API的编程接口,并将原始调用对象作为参数传递到Lambda表达式中。

obj.let { obj2 -> 
	//编写具体的业务逻辑 
 }

代码实践

在这里插入图片描述
?.操作符表示对象为空时什么都不做,不为空时就调用let函数,而let函数会将study对象本身作为参数传递到Lambda表达式中,此时的study对象肯定不为空了,就能放心地调用它的任意方法
精简代码:
当Lambda表达式的参数列表中只有⼀个参数时,可以不用声明参数名,直接使用it关键字来代替即 可

fun doStudy(study: Study?) {
	study?.let {
	it.readBooks()
	it.doHomework()
	}
}

Kotlin中的小魔术

字符串内嵌表达式

Kotlin允许在字符串⾥嵌⼊${}这种语法结构的表达式,并在运行时使用表达式执行的结果替代这一部分内容。
在这里插入图片描述
表达式中仅有一个变量的时候,可以将两边的大括号省略
在这里插入图片描述
在这里插入图片描述

函数的参数默认值

在定义函数的时候给任意参数设定⼀个默认值,这样当调⽤此函数时就不会强制要求调用方为此参数传值,在没有传值的情况下会自动使用参数的默认值。

理想情况

在这里插入图片描述

不太理想情况

在这里插入图片描述
这时就需要用到键值对的方式传参
在这里插入图片描述
键值对的前后顺序无所谓!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值