装箱
基本类型的数组 是没有自动装箱拆箱的过程,而list是有的
对于基本类型使用,推荐用数组,性能会好点
kotlin装箱需要根据场合
对于 Int 这样的基本类型,尽量用不可空变量
var a: Int = 1 // unbox
var b: Int? = 2 // box 会转成 Integer.parse(2) 执行装箱
var list: List<Int> = listOf(1, 2) // box
满足以下条件就不会执行装箱
1 不可空类型
2 使用 IntArray FloatArray数组写法
kotlin 数组不支持协变 就是子类数组对象不能赋值给父类的数组变量
在kotlin 数组的写法变成 泛型式写法 arrayOf("","","")
var array: IntArray = intArrayOf(1, 2) //这种是 unbox 的
kotlin 里用专门的基本类型数组类 (IntArray FloatArray LongArray) 才可以免于装箱拆箱。
元素不是基本类型时,相比 Array,用 List 更方便些
集合
支持协变 指的是:子类类型的list 可以赋值给父类的list
List 以固定顺序存储一组元素,元素可以重复。 ===>listOf("a", "b", "c") 支持协变
Set 存储一组互不相等的元素,通常没有固定 顺序. ===>setOf("a", "b", "c") 支持协变
Map 存储 键-值对的数据集合,键互不相等,但不同的键可以对应相同的值。
var map = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key4" to 3)
map.put("key1", 2)
map["key1"] = 2
val value1 = map.get("key1")
val value2 = map["key2"]
操作函数: forEach filter map flamap
惰性集合sequenceOf 只有使用的使用时候才会执行用数组还是list
list相对于数组 功能更丰富,但是数组基本类型的数组是没有自动装拆箱过程,而list是有的,
对于基本类型 ,数组的性能会比list好.
在kotlin ,要用专门的数组类才能免于自动装拆箱 (IntArray FloatArray LongArray)可变集合/不可变集合
集合分为两种类型:只读的和可变的。这里的只读有两层意思:
- 集合的 size 不可变
- 集合中的元素值不可变
以下是三种集合类型创建不可变和可变实例的例子:
listOf()创建不可变的List,mutableListOf()创建可变的List。setOf()创建不可变的Set,mutableSetOf()创建可变的Set。mapOf()创建不可变的Map,mutableMapOf()创建可变的Map。
toMutable*() 系函数转换成可变的集合,返回的是一个新建的集合,原有的集合还是不可变的,所以只能对函数返回的集合修改。类构造和对象
主构造器的参数声明时加上 var 或者 val,就等价于在类中创建了该名称的属性
1 类默认是public final, 标识符 : 不仅可以表示继承,还可以表示 Java 中的 implement 基类Any
2 构造函数单独用了一个 constructor 关键字来和其他的 fun 做区分。
3 Kotlin 里的类默认是 final 的,是不可继承的 需要open 声明
4 override 是有遗传性的(如果要关闭 子类的方法写成final override机可),open 没有遗传性
5 open 与 abstract关键字区别,abstract修饰的类无法直接实例化,需要被继承
6 类型判断及强转 is 、as、as?(可空类型使用)
7 init 代码块是紧跟在主构造器之后,先于次构造器执行的
8 把主构造器看成身体的头部,那么 init 代码块就是颈部,次构造器就相当于身体其余部分。
9 关键字var val const open constructor overrider abstract Any objcet 修饰的类是单例类 ,(对象申明 对象表达)单例饿汉式的单例,并且实现了线程安全。
10 修饰符
11 kotlin实现匿名类写法
val listener = object: ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
// override
}
}
public:公开,可见性最大,哪里都可以引用。
private:私有,可见性最小,根据声明位置不同可分为类中可见和文件中可见。
protected:保护,相当于 private + 子类可见。
internal:内部,仅对 module 内可见。
@hide 限制客户端访问如果类中有主构造器,那么其他的次构造器都需要通过 this 关键字调用主构造器,可以直接调用或者通过别的次构造器间接调用。如果不调用 IDE 就会报错:
class User constructor(var name: String) {
constructor(name: String, id: Int) {
// 👆这样写会报错,Primary constructor call expected
}
}
从主构造器的特性出发,一旦在类中声明了主构造器,就包含两点必须性:创建类的对象时,不管使用哪个次构造器,
都需要主构造器的参与第一性:
在类的初始化过程中,首先执行的就是主构造器如果把主构造器看成身体的头部,那么 init 代码块就是颈部,
次构造器就相当于身体其余部分。
如果在主构造器的参数声明时加上 var 或者 val,就等价于在类中创建了该名称的属性(property)
并且初始值就是主构造器中该参数的值。常量
class Sample {
companion object {
👇 // 👇
const val CONST_NUMBER = 1
}
}
const val CONST_SECOND_NUMBER = 2 编译期常量
Kotlin 的常量必须声明在对象(包括伴生对象)或者「top-level 顶层」中,因为常量是静态的。
Kotlin 新增了修饰常量的 const 关键字。
Kotlin 中只有基本类型和 String 类型可以声明成常量。
val 只表示只读object、companion object 、top-level
juejin.cn/post/684490… 这里单独作了笔记
如果写工具类 那就直接创建一个文件 在top-level层声明
如果只是针对类,而不是针对外部使用者 就可以写成 companion object 或者object
函数
- 本地函数(嵌套函数)
fun login(user: String, password: String, illegalStr: String) {
👇
fun validate(value: String, illegalStr: String) {
if (value.isEmpty()) {
throw IllegalArgumentException(illegalStr)
}
}
👇
validate(user, illegalStr)
validate(password, illegalStr)
}
这里将共同的验证逻辑放进了嵌套函数 validate 中,
并且 login 函数之外的其他地方无法访问这个嵌套函数。- 返回Unit的函数
- 返回Nothing函数
- 单表达式函数
- 中缀表示法
- 成员函数
- 局部函数(本地函数)
- 泛型函数
- 内联函数
- 扩展函数
- 高阶函数
- 尾递归函数
kotlin内置关键字
any
data 数据类 自动生成getset
inner 内部类申明 区别于嵌套类是可以访问外部类成员
object
sealed
open
constructor
require
inline
suspend 挂起提醒函数 在协程与withContext配套使用
tailrec 搭配尾递归函数,函数的最后一行是函数调用的场景,是递归的一种特殊情形。尾调用不一定是递归调用
(待增加……)
命名参数
fun sayHi(name: String = "world", age: Int) {
...
}
👇
sayHi(age = 21)
在调用函数时,显式地指定了参数 age 的名称,这就是「命名参数」。
Kotlin 中的每一个函数参数都可以作为命名参数。
位置参数
1 与命名参数相对的一个概念被称为「位置参数」,也就是按位置顺序进行参数填写。fun sayHi(name: String = "world", age: Int, isStudent: Boolean = true, isFat: Boolean = true, isTall: Boolean = true) {
...
}
调用 :sayHi(name = "wo", age = 21, isStudent = false, isFat = true, isTall = false)
2 如果函数混用位置参数与命名参数,那么所有的位置参数都应该放在第一个命名参数之前fun sayHi(name: String = "world", age: Int) {
...
}
sayHi(name = "wo", 21) // IDE 会报错,Mixing named and positioned arguments is not allowed
sayHi("wo", age = 21) // 这是正确的写法?. 和 ?:
val str: String? = "Hello"
👇
val length: Int = str?.length ?: -1
意思是如果左侧表达式 str?.length 结果为空,则返回右侧的值 -1
Elvis 操作符还有另外一种常见用法
fun validate(user: User) {
val id = user.id ?: return // 👈 验证 user.id 是否为空,为空时 return
}
// 等同于
fun validate(user: User) {
if (user.id == null) {
return
}
val id = user.id
}泛型通配符
定义:带上界和下界的通配符? 与类型上界不是一个东西 T extends Object
区别:?通配符上下界 一个是能不能读我或能不能写我, 类型上界只是限制类型范围
不支持协变:子类的list泛型是不能赋值给父类list的泛型,引发一个类型擦除概念由于有类型擦除存在,保证类型
的安全,java给泛型设置了这种限制
kotlin也有以上的限制 ,由此 引发出泛型通配符,解决了限制 达到协变
java:
List<? extends TextView> 返回的类型对象 能使用get类似的 但不能set值
List<? super Button> 返回的类型对象 不能使用get一类的 但能set值
kotlin里面变成 :
List<out TextView> out表示 这个参数 只用来输出,不用来输入,只能读我 不能写我
List<in Button> in 表示 这个参数 只用来输入,不用来输出,只能写我,不能读我
还可以用在泛型的类型参数上 interface Proucer<out T> {}
泛型的继承 在kotlin 是 <T> where T : (一个以上继承就需要where关键字)- 使用关键字
out来支持协变,等同于 Java 中的上界通配符? extends。 - 使用关键字
in来支持逆变,等同于 Java 中的下界通配符? super。
Lambda
Kotlin的lambda表达式始终用花括号包围,花括号中通过 箭头(->)将实参和函数体分开,
lambda语法结构:
{x:Int, y:Int -> x+y }