Kotlin class、data class、object、companion object区别
class
class MyActivity (var name:String){
var paramB: String? = null
object MyObject{
const val subName = ""
fun a(){
}
}
}
使用方式
fun main() {
var myActivity = MyActivity("张三")
myActivity.paramB = "麻子"
println(myActivity.name)
println(myActivity.paramB)
println(myActivity.toString())
println("***************")
myActivity = MyActivity("张三")
println(myActivity.name)
println(myActivity.paramB)
println(myActivity.toString())
}
data class[ 数据类 ]:
kotlin中的数据类,只保存一些数据字段,类似于java bean,oc中的model。
写法:注意后面是() ,不是{}
data class yourClassName(
val name: String,
val age: Int,
val sex: Boolean
)
object[ 静态类 ]:
kotlin中使用"object"修饰静态类,可用于util工具类中。
object UserUtil {
val guoji = "中国"
fun getName(): String {
return "小红"
}
}
fun main() {
UserUtil.guoji
UserUtil.getName()
}
class中有 object
class MyActivity {
object MyObject{
const val subName = ""
fun a(){
}
}
}
调用方式:
fun main(){
MyActivity.MyObject.subName
MyActivity.MyObject.a()
}
class中 有companion object:
伴生对象,类似java static 修饰符的作用
class MyActivity {
companion object MyObject {
const val subName = ""
fun a(){
}
}
}
调用方式
fun main(){
MyActivity.subName
MyActivity.a()
}
成员变量get和set方法
属性初始值、getter/setter是可缺省,如果属性类型可以从初始值或getter中推断出来则也可缺省。val类型的属性不具备setter。
属性的getter/setter均可复写,即自定义访问器。如果我们定义了一个自定义的setter,那么每次给属性赋值时都会调用它。
open class Person {
var age: Int = 10
get(){
println("调用get方法了 $field")
return field
}
set(value) {
println("调用set方法了 $value")
//field关键字指向属性本身
field = value
}
}
fun main() {
val p = Person()
println(p.age)
p.age = 30
println(p.age)
}
控制台打印:
调用get方法了 10
10
调用set方法了 30
调用get方法了 30
30
Kotlin 关键字
const的作用范围
const 只能修饰Kotlin的顶级属性(在 .kt 文件中class外声明的属性),或object对象中(object类似Java中的匿名内部类)的属性,并且值被声明为基本类型或String, 表示不可修改的常量。
open关键字
修饰class表示可以被继承;
修饰属性表示可以被重写;
open class Student() {
var name: String? = null
open var age: Int? = null
}
class StudentExt : Student(){
override var age: Int?;
}
lateinit关键字
延时出适化,只能用于修饰属性,修饰符只允许在可变属性上使用(即 var) ,只要保证在使用此属性时已赋值即可,若仍未赋值则会抛出属性尚未初始化异常。
class Student() {
lateinit var name: String
open var age: Int? = null
/**
* 判等属性是否初始化
*/
fun isNameInit():Boolean{
return ::name.isInitialized
}
}
//测试
fun main() {
var student = Student()
if(!student.isNameInit()){
student.name="小天"
}
}
*******by和by lazy
*******inline内联函数
operator重载
fun main() {
val e = Example()
println(e.p)
}
class Example {
var p: String by Delegate()
}
class Delegate {
//重载的修饰符是operator
operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
return "$thisRef, thank you for delegating '${property.name}' to me!"
}
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Any?) {
println("$value has been assigned to '${property.name}' in $thisRef.")
}
}
Kotlin中的内置函数
apply函数
apply函数,属于内联扩展函数,其扩展了所有的泛型对象,在闭包范围内可以任意调用该对象的任意方法,并在最后返回该对象。
源码定义:
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
takeIf、takeUnless
Kotlin标准库提供了 takeIf 函数和 takeUnless 函数. 这些函数允许你在链式调用中加入对象的状态检查。
takeIf : 接收一个判断条件表达式,如果判断表达式为 true 则返回对象本身,false返回 null。
takeUnless: 与 takeIf 相反, 如果判断表达式为 true 则返回 null,false 返回对象本身。
class Student() {
var name: String? = null
var age: Int? = null
}
fun main() {
var student = Student();
student.name = "张三"
println("${student.name} ${student.age}")
//age为不为null都调用apply方法
student.age.apply { doWork(student,15) }
println("apply -> ${student.name} ${student.age}")
//age不为null时调用apply方法
student.age?.apply { doWork(student,10) }
println("?.apply -> ${student.name} ${student.age}")
//takeIf 返回true则执行 doWork
student.age.takeIf { student.age == null}?.apply { doWork(student,18)}
println("takeIf -> ${student.name} ${student.age}")
//takeUnless结果取反,如果为true则执行 doWork
student.age.takeUnless { student.age == null}?.apply { doWork(student,20)}
println("takeUnless -> ${student.name} ${student.age}")
}
fun doWork(student: Student,age:Int){
student.age = age
}
输出结果:
张三 null
apply -> 张三 15
?.apply -> 张三 10
takeIf -> 张三 10
takeUnless -> 张三 20
let函数
属于匿名函数类型,提供了函数式API的编程接口,将原始对象作为参数传递到表达Lamba表达式中,在闭包范围内用it指代原对象;
场景: 对于可空对象的判空处理;对操作对象做一些行为的操作,且通常不改变对象的属性
源码定义:
@kotlin.internal.InlineOnly
public inline fun <T, R> T.let(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
return block(this)
}
//未使用let
val list = listOf(1, 2, 3, 4, 5, 6)
val first = list.first()
println(first+first)
//使用let
val result = listOf(1, 2, 3, 4, 5, 6).let {
it.first()+it.first()
}
println(first+first)
控制台打印结果都是:2
use函数
use 用于确保即使发生错误也可以关闭资源。它只能在实现Closeable接口的对象上调用,使用use
函数,不需要调用close。
源码定义:
public actual inline fun <T : java.lang.AutoCloseable?, R> T.use(block: (T) -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
var exception: Throwable? = null
try {
return block(this)
} catch (e: Throwable) {
exception = e
throw e
} finally {
this.closeFinally(exception)
}
}
also函数
源码定义:
@kotlin.internal.InlineOnly
@SinceKotlin("1.1")
public inline fun <T> T.also(block: (T) -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block(this)
return this
}
符号说明
?说明
"?"加在变量名后,系统在任何情况不会报它的空指针异常。
!!说明
"!!"加在变量名后,如果对象为null,那么系统一定会报异常!
var myList:ArrayList<String>? = null; // 创建一个null的队列
fun main() {
println(myList!!.size)
}
Exception in thread "main" java.lang.NullPointerException
at com.test.service.MainDemoKt.main(MainDemo.kt:12)
at com.test.service.MainDemoKt.main(MainDemo.kt)
?:说明
对象A ?: 对象B 表达式,意思为,当对象 A值为 null 时,那么它就会返回后面的对象 B。
class Student() {
var name: String? = null
var age: Int? = null
}
fun default():String{
return "未设置年龄"
}
fun main() {
var student = Student();
println("${student.age?:default()}")
}
打印:
未设置年龄
:: 说明
Kotlin 中 双冒号操作符 表示把一个方法当做一个参数,传递到另一个方法中进行使用,通俗的来讲就是引用一个方法。
fun main(args: Array<String>) {
println(lock("param1", "param2", ::getResult))
}
/**
* @param str1 参数1
* @param str2 参数2
*/
fun getResult(str1: String, str2: String): String = "result is $str1 , $str2"
/**
* @param p1 参数1
* @param p2 参数2
* @param method 方法名称
*/
fun lock(p1: String, p2: String, methodName: (str1: String, str2: String) -> String): String {
return methodName(p1, p2)
}
控制台打印:
result is param1 , param2
==与===说明
== 表示比较两个值大小。
=== 表示比较对象地址。
***常用集合
泛型
参考文章:www.imooc.com/wiki/kotlin…
基础类型和实参类型
例如对于 List, List 就是基础类型而这里的 String 就是实参类型。
型变
例如 List和List 他们拥有相同的基础类型,实参类型 String 和 Any 存在父子关系,类型替换其实就是型变
fun main(args: Array<String>) {
val stringList: List<String> = listOf("a", "b", "c", "d")
val intList: List<Int> = listOf(1, 2, 3, 4)
printList(stringList)//向函数传递一个List<String>函数实参,也就是这里List<String>是可以替换List<Any>
printList(intList)//向函数传递一个List<Int>函数实参,也就是这里List<Int>是可以替换List<Any>
}
fun printList(list: List<Any>) {
//注意:这里函数形参类型是List<Any>,函数内部是不知道外部传入是List<Int>还是List<String>,全部当做List<Any>处理
list.forEach {
println(it)
}
}
子类型化关系
子类、子类型、超类型概念梳理
任何时候如果需要的是 A 类型值的任何地方,都可以使用 B 类型的值来替换的,那么就可以说 B 类型是 A 类型的子类型或者称 A 类型是 B 类型的超类型。
所有类的非空类型都是该类对应的可空类型的子类型,但是反过来说就不行,就比如 Person
非空类型是 Person?
可空类型的子类型,很明显嘛,任何 Person?
可空类型出现值的地方,都可以使用 Person
非空类型的值来替换。
我们在 Kotlin 开发过程,
** 如果一个函数接收的是一个可空类型的参数,调用的地方传入一个非空类型的实参进去是合法的。
** 但是如果一个函数接收的是非空类型参数,传入一个可空类型的实参编译器就会提示你,可能存在空指针问题,需要做非空判断。因为我们知道非空类型比可空类型更安全。来幅图理解下:
泛型协变 (保留子类型化关系)
基本介绍
来看个例子,String
是 String?
的子类型,我们知道基础类型 List<out E>
是协变的,那么 List<String>
也就是 List<String?>
的子类型的。很明显这里针对的角色就是 List<String>
和 List<String?>
, 是它们保留了 String
到 String?
的子类型化关系。或者换句话说两个具有相同的基础类型的泛型协变类型,如果类型实参具有子类型化关系,那么这个泛型类型具有一致方向的子类型化关系。那么具有子类型化关系实际上子类型的值能在任何时候任何地方替代超类型的值。
基本定义
interface Producer<out T>{//在泛型类型形参前面指定out修饰符
val something: T
fun produce(): T
}
什么是 out 协变点
从上面定义的基本结构来看,实际上协变点就是上面 produce
函数返回值的 T
的位置,Kotlin 中规定一个泛型协变类,在泛型形参前面加上 out 修饰后,那么修饰这个泛型形参在函数内部使用范围将受到限制只能作为函数的返回值或者修饰只读权限的属性。
interface Producer<out T>{//在泛型类型形参前面指定out修饰符
val something: T//T作为只读属性的类型,这里T的位置也是out协变点
fun produce(): T//T作为函数的返回值输出给外部,这里T的位置就是out协变点
}
interface Producer<out T> {
//即使T不是单个的类型,但是它作为一个泛型类型修饰只读属性,所以它所处位置还是out协变点
val something: List<T>
//即使T不是单个的类型,但是它作为泛型类型的类型实参修饰返回值,所以它所处位置还是out协变点
fun produce(): List<Map<String,T>>
}
out 协变点基本特征
协变点基本特征: 如果一个泛型类声明成协变的,用 out 修饰的那个类型形参,在函数内部出现的位置只能在只读属性的类型或者函数的返回值类型。相对于外部而言协变是生产泛型参数的角色,生产者向外输出 out。
泛型逆变 (反转子类型化关系)
基本介绍
逆变实际上就是和协变子类型化关系正好相反,它是反转子类型化关系。
来个例子说明下,我们知道 String
是 String?
的子类型,Comparable<in T>
是逆变的,那么 Comparable<String>
到 Comparable<String?>
实际上是反转了 String
到 String?
的子类型化关系,也就是和 String
到 String?
的子类型化关系相反,那么 Comparable<String?>
就是 Comparable<String>
子类型,Comparable<String>
类型值出现的地方都可用 Comparable<String?>
类型值来替代。
换句话说就是: 两个具有相同的基础类型的泛型逆变类型,如果类型实参具有子类型化关系,那么这个泛型类型具有相反方向的子类型化关系
基本定义
interface Consumer<in T>{//在泛型类型形参前面指定in修饰符
fun consume(value: T)
}
什么是 in 逆变点
从上面定义的基本结构来看,实际上逆变点就是上面 consume
函数接收函数形参的 T
的位置,Kotlin 中规定一个泛型协变类,在泛型形参前面加上 out 修饰后,那么修饰这个泛型形参在函数内部使用范围将受到限制只能作为函数的返回值或者修饰只读权限的属性。
interface Consumer<in T>{//在泛型类型形参前面指定in修饰符var something: T //T作为可变属性的类型,这里T的位置也是in逆变点funconsume(value: T)//T作为函数形参类型,这里T的位置也就是in逆变点}代码块1234
和协变类似,逆变也存在那种泛型类型处于逆变点的位置,这些我们都可以把当做逆变点:
interface Consumer<in T>{var something: B<T>//这里虽然是泛型类型但是T所在位置依然是修饰可变属性类型,所以仍处于逆变点funconsume(value: A<T>)//这里虽然是泛型类型但是T所在位置依然是函数形参类型,所以仍处于逆变点}代码块1234
2.3 in 逆变点基本特征
逆变点基本特征: 如果一个泛型类声明成逆变的,用 in 修饰泛型类的类型形参,在函数内部出现的位置只能是作为可变属性的类型或者函数的形参类型。相对于外部而言逆变是消费泛型参数的角色,消费者请求外部输入 in。
理解Kotlin中的reified关键字
协程
runBlocking和coroutineScope区别
book.kotlincn.net/text/corout…