《Android》Chap.2 入门Kotlin
Posted 我还能码嘛。
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Android》Chap.2 入门Kotlin相关的知识,希望对你有一定的参考价值。
Hello Kotlin
kotlin每一行代码的结尾不需要加分号。
编程之本
变量
关键词
语法规则
代码 | 原词 | 用法 |
---|---|---|
val | value | 声明一个不可变的变量,在初始赋值后不能在被重新赋值 |
var | variable | 声明一个可变的变量,在初始赋值后仍能在被重新赋值 |
代码实践
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)
- 第⼀个次构造函数接收
name
和age
参数,然后它⼜通过this
关键字调用了主构造函数,并将sno
和grade
这两个参数赋值成初始值; - 第⼆个次构造函数不接收任何参数,它通过this关键字调用了第⼀个次构造函数,并将
name
和age
参数也赋值成初始值。 - 第⼆个次构造函数属于间接调⽤主构造函数。
经过上面的代码现在有三种方式对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() 的配套⽅法,也需要⼀起重写,否则会导致HashMap 、HashSet 等hash相关的系统类 ⽆法正常⼯作 |
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
表示不可为空
以上是关于《Android》Chap.2 入门Kotlin的主要内容,如果未能解决你的问题,请参考以下文章
最全Android Kotlin入门教程(Kotlin 入门指南高级Kotlin强化实战Kotlin协程进阶实战)
Android:Kotlin详细入门学习指南-高阶函数-基础语法
Android:Kotlin详细入门学习指南-高阶函数-基础语法