1. 方法中的参数 有var/val 与没有的区别(fun A(var name: String) / fun A(name: String))???
有var/val: 表示是当前类的一个属性
无var/val: 表示是当前方法的一个参数
2. Kotlin中各种声明的执行顺序?
初始化顺序依次如下:
1. 构造函数里声明的属性
2. 类级别的属性赋值
3. init初始化代码块里的属性赋值和函数调用
4. 次构造函数里的属性赋值和函数调用
3. 继承: 使用open关键字修饰的类和方法可以被继承, 否则类不能继承, 方法不能重写
4. inner class; 定义内部类, 在内部类中访问外部类, 需要显示使用this@xxx.yyy() (xxx是外部类名, yyy是外部类的方法名)
5. data class: 数据类
6. 拓展函数: fun AAA.bbb(){......} (AAA是需要拓展的类, bbb是拓展的方法)
属性拓展: 同上
7. 操作符/关键字:
==: 比较得是内容
===: 比较的是内存堆上的对象
8.关键字相关
by lazy: 懒加载, 通过by关键字, 将xxx属性委托给lazy()里面的实例
lateinit: 延迟初始化
const: 用来修饰val (不能是var)常量
range: 一个值是否在一个取值范围里, 例如 4 in 0..10, 即4是否在0-10的范围内
apply: 对一个函数(对象(接收者)进行配置, 然后返回这个配置好的对象 ------> 返回配置好的对象(接收者)
let: 会将接收者传给lambda, 匿名函数执行完 ------> 返回lambda的最后一行
run: 类似apply ------> 返回lambda的结果, 即布尔值true/false或者其他值
with: 需要传入一个参数 其他类似run
also: 类似let ------> 返回配置好的对象
takeIf: 判断lambda的条件表达式, 如果是true:返回接收者对象, false:返回null
takeUnless: 与takeIf相反
take:
is: 类型判断
as: 类型转换
by: 实现委托, 类委托和属性委托, 可以更好的实现代理模式
interface Animal{
fun bark()
}
class Cat: Animal{
override fun fark(){
println("喵喵")
}
}
class Zoo(animal: Animal) : Animal by animal //将Zoo委托给他的参数 animal
fun main(args: Array<String>){
val cat = Cat()
Zoo(cat).frak()
}
输出结果: "喵喵"
map: 变换函数, 会遍历接收者集合中的每一个元素, 让百能唤起函数作用于集合中的每一个元素, 返回结果是包含已修改元素的集合, 会作为链上 下一个函数的输入
flatMap: 操作一个集合的集合, 可以将多个集合中的元素合并后返回一个包含所有元素的单一集合
filter: 过滤--> 例如可以将一个集合中包含"a"字母的元素拿出来形成一个新的集合, 该集合中的每一个元素都包含"a"
in : 父类泛型对象可以赋值给子类泛型对象
out: 子类泛型对象可以赋值给父类泛型对象
constructor(name: String){......}: 次构造器, 写在类中(如果写在类名后 则是主构造器)
object: 1. 对象表达式 2. 对象声明(object修饰的类为静态类, 里面的方法和变量也都为静态的)
companion object{} 伴生对象 在类中只能存在一个,即 {}中的方法和属性是静态的, 类中的其他属性和方法则不是静态的
? //可控类型 表示对象/属性可以null
?. //安全调用运算符 如果问号(?)前的值不为空可以执行点(.)后的方法, 否则返回null
?: //空合并操作符, A ?: B 即如果A是空, 返回B,否则返回A它本身
:: //函数引用, ::后面跟函数
!!: //当值为null就抛出异常, 不执行后面的方法
------> 在java调用kotlin方法 <------
@file:JvmName("xxx"): 在Kotlin文件最上面添加这个注释, 则可以不使用当前Kotlin文件名字, 而是使用这里定义的名字(xxx: 别名)
@JvmField: 在Kotlin文件中 属性上面添加这个注释, 那么在Java文件中可以直接使用这个属性, 而不用get/set
@JvmStatic: 在Kotlin文件中 方法上面添加这个注释, 那么在Java文件中可以直接使用这个方法, (静态调用 class.xx() 类.方法)
@JvmOverloads: 在Kotlin文件中 方法上面添加这个注释, 那么在Java文件中使用, 就可以强制实现重载, 传入不同数量的参数.
@Throws(IOException::class): 在Kotlin文件中 方法上面添加这个注释, 那么在Java文件中使用, 就必须要处理这个异常添加try{}catch() (异常可以是任意异常)
Kotlin 泛型
泛型类:
制作一个万能的遥控器
class Controller<T>(){
fun turnOn(obj: T){......}
fun turnOff(obj: T){......}
}
电视遥控器:
val tvController = Controller<TV>()
val tv = TV()
tvController.turnOn(tv)
tvController.turnOff(tv)
电风扇遥控器:
val fanController = Controller<Fan>()
val fan = Fan()
fanController.turnOn(fan)
fanController.turnOff(fan)
泛型函数:
fun <T> turnOn(obj: T){......}
fun <T> turnOff(obj: T){......}
控制电视:
val tv = TV()
turnOn<TV>(tv)
turnOff<TV>(tv)
控制风扇:
val fan = Fan()
turnOn<Fan>(fan){......}
turnOff<Fan>(fan){......}
泛型的不变性:
招聘大学生的故事
open class Student() //学生类
class FemaleStudent: Student() //女学生类
class University<T>(val name: String){
fun get(): T{......} //往外取, 表示招聘
fun put(student: T){......}
}
开始招聘
lateinit var university: University<Student>
university = University<Student>("某大学")
val student: Student = university.get() //招聘(得到一个大学生)
错误写法:
university = University<FemaleStudent>("女子大学")
val student: Student = university.get()
reason:
required: University<Student>
Found: University<FemaleStudent>
那怎么可以执行上面的方式呢? 利用泛型的协变, 即 T 前加out (去掉了put()方法)
class University<out T>(val name: String){
fun get(): T{......}
}
val university: University<Student> = University<FemaleStudent>("女子大学")
val student: Student = university.get()
泛型的逆变, 在T 前加in
open class Student()
class FemaleStudent: Student()
class Unitversity<T>(val name: String){
fun get(): T{......}
fun put(student: T){......} //这里是填志愿
}
val sister: FemaleStudent = FemaleStudent()
val university: University<FemaleStudent> = Unitversity<FemaleStudent>("女子大学")
university.put(sister)
那如果想报普通大学呢?
val sister: FemaleStudent = FemaleStudent()
val university: Unitversity<FemaleStudent> = Unitversity<Student>("普通大学")
university.put(sister)
报错:
Required: Unitversity<FemaleStudent>
Found: Unitversity<Student>
那怎么可以执行上面的方式呢? 利用泛型的逆变, 即 T 前加in (去掉了get()方法)
class University<in T>(val name: String){
fun put(student: T){......}
}
val sister: FemaleStudent = FemaleStudent()
val university: University<FemaleStudent> = Unitversity<Student>("普通大学")
university.put(sister)
使用处型变(使用处协变, 使用处逆变)
open class Student()
class FemaleStudent: Student()
class University<T>(val name:String){
fun get(): T{......}
fun put(student: T){......}
}
使用处协变
fun useSiteCovariant(university: Unitversity<out Student>){
val femaleStudent: Student? = university.get()
}
使用处逆变
fun useSiteCovariant(university: University<in FemaleStudent>){
university.put(FemaleStudent())
}
扩展函数
1. 顶层扩展(本质: 就是Java的静态方法, 为当前类添加静态方法或者属性):
fun String.log(){ //为String 定义扩展函数
println(this)
}
val String?.isNullOrBlank: Boolean //为String 定义扩展属性
get() = this == null || this.isBlank()
2. 类内扩展:
class Host(val hostName: String){
fun printlnHostName(){println(hostName)}
}
class Test(val host: Host, val port: Int){
fun printlnPort(){println(port)}
//在Test类中给Host增加一个扩展函数
fun Host.printlnConnectionString(){
printlnHostName() //Host类中的函数
print(":")
printPort() //Test类中的函数
}
//在Test类中给Host增加了一个属性
val Host.isHomeEmpty: Boolean
get() = hostName.isEmpty()
fun test(){
host.printlnConnectionString()
}
}
//在别的地方调用Host类的扩展函数
fun main(){
Host("").isHomeEmpty
Host("").printlnConnectionString()
}
以上会报错, 因为Host的类的扩展在外面是无法访问的, 这与顶层扩展不同
顶层扩展: 不能定义在类内, 他的作用域是package级别, 能导包就能用(也就是说需要导包)
类内拓展: 定义在其他类内, 他的作用域是该类中, 优势是既能访问被扩展类(Host)也能访问他所在的类(Test)
扩展并没有实际修改被扩展的类, 因此我们仍然只能访问类中的public方法和属性. <<原理是 被扩展的类变成了扩展函数的参数>>