文章目录
Kotlin概述
- 一种在Java虚拟机上运行的静态类型编程语言
- 可以和Java代码相互操作
- 容易在Android项目中代替Java或者同Java一起使用
- 在2019年的Google I/O大会上Kotlin被选为Android开发者首选语言
Kotlin特点
- 简洁易用
- 安全
- 互操作性
- 工具友好
Kotlin设计理念
- Readability
- Reuse
- Interoperablity
- Safety
- Tooling
Kotlin构建流程
Kotlin必备基础
Kotlin基本数据类型
Kotlin的数组

生成数组的方式
/**
* 数组
*/
fun arrayType() {
//arrayOf
val array = arrayOf(1, 2, 3)
//arrayOfNulls
val array1 = arrayOfNulls<Int>(3)
array1[0] = 4
array1[1] = 5
array1[2] = 6
//Array(5)的构造函数
val array2 = Array(5) { i -> (i * i).toString() }
// intArrayOf(),doubleArrayOf()
val x: IntArray = intArrayOf(1, 2, 3)
println("x[0] + x[1] = ${x[0] + x[1]}")
// 大小为 5、值为 [0, 0, 0, 0, 0] 的整型数组
val array3 = IntArray(5)
// 例如:用常量初始化数组中的值
// 大小为 5、值为 [42, 42, 42, 42, 42] 的整型数组
val array4 = IntArray(5) { 42 }
// 例如:使用 lambda 表达式初始化数组中的值
// 大小为 5、值为 [0, 1, 2, 3, 4] 的整型数组(值初始化为其索引值)
val array5 = IntArray(5) { it * 1 }
println(array5[4])
遍历数组的5种方式
/****遍历数组的常用5中方式****/
//数组遍历
for (item in array) {
println(item)
}
//带索引遍历数组
for (i in array.indices) {
println("$i -> ${array[i]}")
}
// 遍历元素(带索引)
for ((index, item) in array.withIndex()) {
println("$index -> $item ")
}
//forEach遍历数组
array.forEach { println(it) }
// forEach增强版
array.forEachIndexed { index, item ->
println("$index -> $item ")
}
}
Kotlin的集合

- 集合的可变性与不可变性
在Kotlin存在两种意义上的集合,一种是可以修改的一种是不可修改的

每个不可变集合都有对应的可变集合,也就是以mutable为前缀的集合。 - 测试代码
/**
* 集合
*/
fun collectionType() {
//不可变集合
val stringList = listOf("one", "two", "one")
println(stringList)
val stringSet = setOf("one", "two", "one")
println(stringSet)
//可变集合
val numbers = mutableListOf(1, 2, 3, 4)
numbers.add(5)
numbers.removeAt(1)
numbers[0] = 0
println(numbers)
val hello = mutableSetOf("H", "e", "l", "l", "o")//自动过滤重复元素
hello.remove("o")
println(hello)
//集合的加减操作
hello += setOf("w", "o", "r", "l", "d")
println(hello)
/**Map<K, V> 不是 Collection 接口的继承者;但是它也是 Kotlin 的一种集合类型**/
val numberMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 4, "key5" to 5)
println("All keys:${numberMap.keys}")
println("All values:${numberMap.values}")
if ("key2" in numberMap) println("Value by key key2:${numberMap["key2"]}")
if (1 in numberMap.values) println("1 is in the map")
if (numberMap.containsValue(1)) println("1 is in the map")
/**
* Q1:两个具有相同键值对,但顺序不同的map相等吗?为什么?
* 无论键值对的顺序如何,包含相同键值对的两个 Map 是相等的
* 具体原因可参看equals源码
*/
val anotherMap = mapOf("key2" to 2, "key1" to 1, "key3" to 3, "key4" to 4, "key5" to 5)
println("anotherMap == numberMap:${anotherMap == numberMap}")
anotherMap.equals(numberMap)
/**
* Q2:两个具有相同元素,但顺序不同的list相等吗?为什么?
*/
}
集合排序
/**
* 集合排序
*/
fun collectionSort() {
val number3 = mutableListOf(1, 2, 3, 4)
//随机排序
number3.shuffle()
println(number3)
number3.sort()//从小到大
number3.sortDescending()//从大到小
println(number3)
//条件排序
data class Language(var name: String, var score: Int)
val languageList: MutableList<Language> = mutableListOf()
languageList.add(Language("Java", 80))
languageList.add(Language("Kotlin", 90))
languageList.add(Language("Dart", 99))
languageList.add(Language("C", 80))
//使用sortBy进行排序,适合单条件排序
languageList.sortBy { it.score }
println(languageList)
//使用sortWith进行排序,适合多条件排序
languageList.sortWith(compareBy({
//it变量是lambda中的隐式参数
it.score
}, { it.name }))
println(languageList)
}
Kotlin方法与Lambda表达式
- 方法声明

- 测试代码
/**---------方法声明---------**/
fun functionLearn(days: Int): Boolean {
Person().test1()
Person.test2()
println("NumUtil.double(2):${NumUtil.double(2)}")
println("double(3):${double(3)}")
return days > 100
}
class Person {
/**
* 成员方法
*/
fun test1() {
println("成员方法")
}
/**
* 类方法:Kotlin中并没有static关键字,不过我们可以借助companion object 来实现类方法的目的
*/
companion object {
fun test2() {
println("companion object 实现的类方法")
}
}
}
/**
* 整个静态类
*/
object NumUtil {
fun double(num: Int): Int {
return num * 2
}
}
/**
* 全局静态直接新建一个 Kotlin file 然后定义一些常量、方法
*/
/**
* 单表达式方法,当方法返回单个表达式时,可以省略花括号并且在 = 符号之后指定代码体即可:
*/
fun double(x: Int): Int = x * 2
/**
* 当返回值类型可由编译器推断时,显式声明返回类型是可选的:
*/
fun double2(x: Int) = x * 2
/**---------方法参数---------**/
/**
* 默认值,方法参数可以有默认值,当省略相应的参数时使用默认值。与其Java相比,这可以减少重载数量:
*/
fun read(b: Array<Byte>, off: Int = 0, len: Int = b.size) { /*……*/
}
/**
* 如果一个默认参数在一个无默认值的参数之前,那么该默认值只能通过使用具名参数调用该方法来使用:
*/
fun foo(bar: Int = 0, baz: Int) {}
fun defaultValue() {
foo(baz = 1) // 使用默认值 bar = 0
foo(1) { println("hello") }// 使用默认值 baz = 1
foo(qux = { println("hello") })// 使用两个默认值 bar = 0 与 baz = 1
foo { println("hello") }
}
/**
* 如果在默认参数之后的最后一个参数是 lambda 表达式,那么它既可以作为具名参数在括号内传入,也可以在括号外传入
*/
fun foo(bar: Int = 0, baz: Int = 1, qux: () -> Unit) {} // 使用两个默认值 bar = 0 与 baz = 1
/**
* 可变数量的参数(Varargs)
*/
fun append(vararg str: Char): String {
val result = StringBuffer()
for (char in str) {
result.append(char)
}
return result.toString()
}
fun testVarargs() {
println(append('h', 'e', 'l', 'l', 'o'))
val world = charArrayOf('w', 'o', 'r', 'l', 'd')
val result = append('h', 'e', 'l', 'l', 'o', ' ', *world)
println(result)
}
/**---------方法作用域---------**/
/**
* 局部方法
*/
fun magic(): Int {
fun foo(v: Int): Int {
return v * v
}
val v1 = (0..100).random()
return foo(v1)
}
Lambda表达式
- Lambda表达式的特点

- Lambda语法

- 测试代码
/**---------Lambda表达式---------**/
fun bindListener(view: View) {
view.setOnClickListener { v ->
Toast.makeText(v.context, "Lambda简洁之道", Toast.LENGTH_LONG).show()
}
}
/**
* 无参数的情况
*/
// 原方法
fun test() {
println("无参数")
}
// lambda代码
val test1 = { println("无参数") }
/**
* 有参数的情况,这里举例一个两个参数的例子,目的只为大家演示
*/
// 源代码
fun test2(a: Int, b: Int): Int {
return a + b
}
// lambda
val test3: (Int, Int) -> Int = { a, b -> a + b }
// 或者
val test4 = { a: Int, b: Int -> a + b }
/**
* lambda表达式作为方法中的参数的时候
*/
// 源代码
fun test(a: Int, b: Int): Int {
return a + b
}
fun sum(num1: Int, num2: Int): Int {
return num1 + num2
}
// lambda
fun test(a: Int, b: (num1: Int, num2: Int) -> Int): Int {
return a + b(3, 5)
}
- 认识it

- 测试代码
//如何使用it
fun test1() {
// 这里举例一个语言自带的一个高阶方法filter,此方法的作用是过滤掉不满足条件的值。
val arr = arrayOf(1, 3, 5, 7, 9)
// 过滤掉数组中元素小于2的元素,取其第一个打印。这里的it就表示每一个元素。
println(arr.filter { it < 5 }.component1())
//结合上文的
testClosure(1)(2) {
println(it)
}
}
- 使用 _

Kotlin方法进阶
- 高阶函数
- 函数作为参数
- 函数作为返回值
- 实战:实现一个能够对集合元素进行求和的高阶函数,并且每遍历一个集合元素要有回调
/**
* 高阶函数-函数作为参数
**/
//这是一个求List<Int>元素和的扩展,它因为接受一个函数类型的callback参数而被成为高阶函数
fun List<Int>.sum(callback: (Int) -> Unit): Int {
var result = 0
for (v in this) {
result += v
callback(v)
}
return result;
}
- 实战: 实现一个能够对集合元素进行求和的高阶函数,并且返回一个声明为(scale:Int) -> Float的函数
/**
* 高阶函数-函数作为返回值
**/
//这是一个求List<String>元素和的扩展,它返回一个(scale: Int) -> Float 函数而被成为高阶函数
fun List<String>.toIntSum(): (scale: Int) -> Float {
println("第一层函数")
return fun(scale): Float {
var result = 0f
for (v in this) {
result += v.toInt() * scale
}
return result
}
}
闭包(Closure)
-
闭包可以理解为能够读取其他方法内部变量的方法
-
闭包是将方法内部和外部连接起来的桥梁
闭包的特性:- 方法可以作为另一个方法的返回值或参数,还可以作为一个变量的值
- 方法可以嵌套定义,即在一个方法内部定义另一个方法
闭包的好处
1. 加强模块化
2. 抽象
3. 灵活
4. 简化代码
- 实战

- 代码
/**
* 闭包,testClosure接收一个Int类型的参数,返回一个带有如下参数的方法`(Int, (Int) -> Unit)`
* ,该方法第一个参数是Int类型,第二个参数是一个接收Int类型参数的方法;
* testClosure也是高阶方法
*/
fun testClosure(v1: Int): (v2: Int, (Int) -> Unit) -> Unit {
return fun(v2: Int, printer: (Int) -> Unit) {//匿名方法
printer(v1 + v2)
}
//难度较高,慢慢理解,参考lambda
}
- 解构声明
/**
* 解构声明
*/
data class Result(val msg: String, val code: Int)
fun testDeco() {
var result = Result("success", 0)
result = result()
val (msg, code) = result
println("msg:${msg}")
println("code:${code}")
}
fun result(): Result {
return Result("good", 1)
}
- 函数字面值
/**
* 方法字面值
*/
//定义了一个变量 tmp,而该变量的类型就是 (Int) -> Boolean
var tmp: ((Int) -> Boolean)? = null
fun literal() {
// { num -> (num > 10) }即是一个方法字面值
tmp = { num -> (num > 10) }
}
Kotlin类与接口

- 类与接口

- 测试代码
fun main() {
// InitDemo("test")
// Animal(22)// Kotlin 中没有“new”关键字
testDataUtil()
}
/**---------构造方法---------**/
/**
* 主构造方法
*/
class KotlinClass constructor(name: String) { /*……*/ }
//没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字
class KotlinClass1(name: String) { /*……*/ }
class InitDemo(name: String) {
/**
* 关于:.also(::println)的理解
* ::表示创建成员引用或类引用,通过它来传递println方法来作为also的参数
* 那also又是什么呢?源码跟进 -> Kotlin扩展
*/
val firstProp = "First Prop: $name".also(::println)
init {
println("First initializer block that prints $name")
}
val secondProp = "Second Prop: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
//构造方法的参数作为类的属性并赋值,KotlinClass2在初始化时它的name与score属性会被赋值
class KotlinClass2(val name: String, var score: Int) { /*……*/ }
/**
* 次构造方法
*/
class KotlinClass3(val view: View) {
var views: MutableList<View> = mutableListOf()
init {//初始化代码块会在次构造方法之前执行
println("init")
}
constructor(view: View, index: Int) : this(view) {
views.add(view)
println("constructor")
}
}
/**---------继承与覆盖---------**/
open class Animal(age: Int) {
init {
println(age)
}
open val foot: Int = 0
open fun eat() {
}
}
/**
* 继承
*/
//派生类有主构造方法的情况
class Dog(age: Int) : Animal(age) {
override val foot = 4
override fun eat() {
}
}
//派生类无柱构造方法的情况
class Cat : Animal {
constructor(age: Int) : super(age)
}
/**---------属性的声明---------**/
class Shop {
var name: String = "Android"
var address: String? = null
val isClose: Boolean
get() = Calendar.getInstance().get(Calendar.HOUR_OF_DAY) > 11
var score: Float = 0.0f
get() = if (field < 0.2f) 0.2f else field * 1.5f
set(value) {
println(value)
}
}
fun copyShop(shop: Shop): Shop {
val shop = Shop()
shop.name = shop.name
shop.address = shop.address
// ……
return shop
}
/**---------延迟初始化属性与变量---------**/
class Test {
lateinit var shop: Shop
fun setup() {
shop = Shop()
}
fun test() {
if (::shop.isInitialized)
println(shop.address)
}
}
/**---------抽象类---------**/
abstract class Printer {
abstract fun print()
}
class FilePrinter : Printer() {
override fun print() {
}
}
/**---------接口---------**/
interface Study {
var time: Int// 抽象的
fun discuss()
fun earningCourses() {
println("Android 架构师")
}
}
//在主构造方法中覆盖接口的字段
class StudyAS(override var time: Int) : Study {
//在类体中覆盖接口的字段
// override var time: Int = 0
override fun discuss() {
}
}
/**---------解决覆盖冲突---------**/
interface A {
fun foo() {
println("A")
}
}
interface B {
fun foo() {
print("B")
}
}
class D : A, B {
override fun foo() {
super<A>.foo()
super<B>.foo()
}
}
/**---------数据类---------**/
data class Address(val name: String, val number: Int) {
var city: String = ""
fun print() {
println(city)
}
}
fun testAddress() {
val address = Address("Android", 1000)
address.city = "Beijing"
val (name, city) = address
println("name:$name city:$city")
}
/**---------对象表达式---------**/
open class Address2(name: String) {
open fun print() {
}
}
class Shop2 {
var address: Address2? = null
fun addAddress(address: Address2) {
this.address = address
}
// 私有方法,所以其返回类型是匿名对象类型
private fun foo() = object {
val x: String = "x"
}
// 公有方法,所以其返回类型是 Any
fun publicFoo() = object {
val x: String = "x"
}
fun bar() {
val x1 = foo().x // 没问题
// val x2 = publicFoo().x // 错误:未能解析的引用“x”
}
}
fun test3() {
//如果超类型有一个构造方法,则必须传递适当的构造方法参数给它
Shop2().addAddress(object : Address2("Android") {
override fun print() {
super.print()
}
})
}
//只需要一个简单对象
fun foo() {
val point = object {
var x: Int = 0
var y: Int = 0
}
print(point.x + point.y)
}
/**---------对象声明---------**/
object DataUtil {
fun <T> isEmpty(list: ArrayList<T>?): Boolean {
return list?.isEmpty() ?: false
}
}
fun testDataUtil() {
val list = arrayListOf("1")
println(DataUtil.isEmpty(list))
}
/**---------伴生对象---------**/
class Student(val name: String) {
companion object {
val student = Student("Android")
fun study() {
println("Android 架构师")
}
}
var age = 16
fun printName() {
println("My name is $name")
}
}
fun testStudent() {
println(Student.student)
Student.study()
}
Kotlin中的泛型
泛型约束

- 测试代码
fun main() {
// Coke().price(Sweet())
// BlueColor(Blue()).printColor()
testSum()
}
/**---------泛型接口---------**/
interface Drinks<T> {
fun taste(): T
fun price(t: T)
}
open class Sweet {
val price = 5
}
class Coke : Drinks<Sweet> {
override fun taste(): Sweet {
println("Sweet")
return Sweet()
}
override fun price(t: Sweet) {
println("Coke price:${t.price}")
}
}
/**---------泛型类---------**/
abstract class Color<T>(var t: T/*泛型字段*/) {
abstract fun printColor()
}
class Blue {
val color = "blue"
}
class BlueColor(t: Blue) : Color<Blue>(t) {
override fun printColor() {
println("color:${t.color}")
}
}
/**---------泛型方法---------**/
fun <T> fromJson(json: String, tClass: Class<T>): T? {
/*获取T的实例*/
val t: T? = tClass.newInstance()
return t
}
/**---------泛型约束---------**/
fun <T : Comparable<T>?> sort(list: List<T>?){}
fun test12() {
sort(listOf(1, 2, 3)) // OK,Int 是 Comparable<Int> 的子类型
// sort(listOf(Blue())) // 错误:Blue 不是 Comparable<Blue> 的子类型
}
//多个上界的情况
fun <T> test(list: List<T>, threshold: T): List<T>
where T : CharSequence,
T : Comparable<T> {
return list.filter { it > threshold }.map { it }
}
/**---------泛型中的out与in---------**/
fun sumOfList(list: List<out Number>): Number {
var result = 0f
for (v in list) {
result += v.toFloat()
}
return result
}
fun testSum() {
val result = sumOfList(listOf(1.5, 2, 3, 5.5))
println("sumOfList:$result")
}
Kotlin中的协变与逆变
在Kotlin中out代表协变,in代表逆变,为了加深理解 我们可以将Kotlin的协变看成Java的上界通配符,逆变看成Java的下界通配符:

- 总结
Java泛型类型 | Java代码 | Kotlin代码 | Kotlin泛型类型|

- Kotlin泛型更加简洁安全,但是和Java一样都是有类型擦除的,都属于编译时泛型。
Kotlin注解

@Target
@Target顾名思义就是目标对象,也就是我们定义的注解能够应用于哪些目标对象,可以同时指定多个作用的目标对象。
- @Target的原型

- AnnotationTarget枚举类源码
public enum class AnnotationTarget {
/** Class, interface or object, annotation class is also included */
CLASS,
/** Annotation class only */
ANNOTATION_CLASS,
/** Generic type parameter (unsupported yet) */
TYPE_PARAMETER,
/** Property */
PROPERTY,
/** Field, including property's backing field */
FIELD,
/** Local variable */
LOCAL_VARIABLE,
/** Value parameter of a function or a constructor */
VALUE_PARAMETER,
/** Constructor only (primary or secondary) */
CONSTRUCTOR,
/** Function (constructors are not included) */
FUNCTION,
/** Property getter only */
PROPERTY_GETTER,
/** Property setter only */
PROPERTY_SETTER,
/** Type usage */
TYPE,
/** Any expression */
EXPRESSION,
/** File */
FILE,
/** Type alias */
@SinceKotlin("1.1")
TYPEALIAS
}
@Retention

- AnnotationRetention源码
public enum class AnnotationRetention {
/** Annotation isn't stored in binary output */
SOURCE,
/** Annotation is stored in binary output, but invisible for reflection */
BINARY,
/** Annotation is stored in binary output and visible for reflection (default retention) */
RUNTIME
}
- 要想在反射中可见,注解必须修饰成Runtime类型
- 注解的使用场景

- 案例: 自定义注解实现API调用是的请求方法检查
fun main() {
fire(ApiGetArticles())
}
//和一般的声明很类似,只是在class前面加上了annotation修饰符
@Target(AnnotationTarget.CLASS)
annotation class ApiDoc(val value: String)
/**
* 自定义注解实现API调用时的请求方法检查
*/
@ApiDoc("修饰类")
class Box {
// @ApiDoc("修饰字段")
val size = 6
// @ApiDoc("修饰方法")
fun test() {
}
}
public enum class Method {
GET,
POST
}
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class HttpMethod(val method: Method)
interface Api {
val name: String
val version: String
get() = "1.0"
}
@HttpMethod(Method.POST)
class ApiGetArticles : Api {
override val name: String
get() = "/api.articles"
}
fun fire(api: Api) {
val annotations = api.javaClass.annotations
val method = annotations.find { it is HttpMethod } as? HttpMethod
println("通过注解得知该接口需需要通过:${method?.method} 方式请求")
}
Kotlin扩展(Extensions)技术与应用

- 扩展方法的原型

- 测试代码
fun main() {
val test = mutableListOf(1, 2, 3)
test.swap(1, 2)
println(test)
}
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
- 泛型扩展方法与扩展属性
fun main() {
val test = mutableListOf(1, 2, 3)
test.swap(1, 2)
println(test)
//在被扩展类的外部使用
Jump().doubleJump(2f)
Jump().test()
val test2 = mutableListOf("Android Q", "Android N", "Android M")
test2.swap1(0, 1)
println(test2)
val listString = listOf("Android Q", "Android N", "Android M")
println("listString.last${listString.last}")
Jump.print("伴生对象的扩展")
}
fun MutableList<Int>.swap(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
class Jump {
companion object {}
fun test() {
println("jump test")
//在被扩展类的内部使用
doubleJump(1f)
}
}
fun Jump.doubleJump(howLong: Float): Boolean {
println("jump:$howLong")
println("jump:$howLong")
return true
}
//泛型化扩展函数
fun <T> MutableList<T>.swap1(index1: Int, index2: Int) {
val tmp = this[index1]
this[index1] = this[index2]
this[index2] = tmp
}
/**---------扩展属性---------**/
//为String添加一个lastChar属性,用于获取字符串的最后一个字符
val String.lastChar: Char get() = this.get(this.length - 1)
///为List添加一个last属性用于获取列表的最后一个元素,this可以省略
val <T>List<T>.last: T get() = get(size - 1)
/**---------为伴生对象添加扩展---------**/
fun Jump.Companion.print(str: String) {
println(str)
}
Kotlin中常用的扩展
- let扩展

- 测试代码
/**
* let
*/
fun testLet(str: String?) {
//限制str2的作用域
str.let {
val str2 = "let扩展"
println(it + str2)
}
// println(str2)//报错
//避免为null的操作
str?.let {
println(it.length)
}
}
- run扩展

- apply扩展

- 测试代码
/**
* apply
*/
fun testApply() {
ArrayList<String>().apply {
add("1")
add("2")
add("3")
println("$this")
}.let { println(it) }
}
- 使用Kotlin扩展为控件绑定监听器减少模板代码
/**---------案例:使用Kotlin扩展为控件绑定监听器减少模板代码---------**/
//为Activity添加find扩展方法,用于通过资源id获取控件
fun <T : View> Activity.find(@IdRes id: Int): T {
return findViewById(id)
}
//为Int添加onClick扩展方法,用于为资源id对应的控件添加onClick监听
fun Int.onClick(activity: Activity, click: () -> Unit) {
activity.find<View>(this).apply {
setOnClickListener {
click()
}
}
}
Kotlin实用技巧
使用Kotlin安卓扩展,想findViewById说拜拜
- 在模块的 build.gradle文件中启用Gradle安卓扩展插件即可:
apply plugin: 'kotlin-android-extensions'
可以直接通过控件Id来访问控件实例
字符串的空判断,向TextUtils.isEmpty说拜拜
- Kotlin中 CharSequence类的扩展方法isNullOrEmpty能帮我们省去对TextUtils.isEmpty的使用

- 除了isNullOrEmpty扩展之外,CharSequence还有个isNullOrBlank扩展,如果name都是空格,则TextUtils.isEmpty不满足使用。但isNullOrBlank仍可用。
使用@JvmOverloads告别繁琐的构造函数 重载
在Kotlin中@JvmOverloads注解的作用就是: 在有默认参数值的方法中使用@JvmOverloads注解,则Kotlin就会暴露多个重载方法。这对我们自定义控件时特别有用。