知识点(持续更新)-2022.06.28

317 阅读9分钟

面向对象的三大特征

  • 多态
  • 封装
  • 继承

面向对象的五大基本原则

  • 单一职责原则
  • 开放封闭原则
  • 里氏替换原则
  • 接口隔离原则
  • 依赖倒置原则

Activity的启动模式

在AndroidManifest.xml设置时,只允许设置一种

  • standard 默认模式:每创建一个Activity都回向栈中加入一个Activity
  • singleTop 栈顶模式:只要栈顶的Activity是我们需要创建的Activity,那就会直接复用栈顶的Activity使用
  • singleTask 栈中复用模式:只要栈中存在Activity是我们需要创建的Activity,将上面的Activity弹出,并那就将该Activity置顶.
  • singleInstance 单例模式:创建一个新的栈(Task),只用于存放该Activity 在代码动态设置Intent时,可以多个组合

String 、 StringBuffer、StringBuilder之间的区别

String 源码是有关键final的,所以赋值后是不可变的,我们平常用的时候,第二次赋值,其实是创建一个新的String对象

String x = "你好";
x  = "我是小柴";

StringBuffer,值是可以变的,不会产生新的对象,线程安全,可以多线程操作一个字符串 StringBuilder,值是可以变的,不会产生新的对象,线程不安全,建议单线程操作字符串,他的速度比StringBuffer更快。

ANR

ANR是应用无响应的

  • 在主线程超过5秒、广播超过10秒、服务超过20秒
  • 后台广播超过60秒、后台服务超过200秒、内容提供者超过10秒
  • 的延时操作时,应用就会报ANR应用无响应
  • 处理方法很明确,不要做超过规定时间的延时操作 具体方法如下 UI线程尽量只做更新UI的事 使用子线程处理IO操作、数据库操作、连接网络等有可能延时的操作 若广播需要做延时操作,在IntentService线程进行延时操作

Android系统架构(五层)

  • 应用层
  • 应用框架层
  • 系统运行库层
  • 硬件抽象层
  • Linux内核层

透明度列表

透明度 16进制表示 如:00FFFFFF 是百分百透明的白色,00就是ARGB中的Alpa

透明度% A的16进制数 100% 00 95 % 0D 90 % 1A 85 % 26 80 % 33 75 % 40 70 % 4D 65 % 59 60 % 66 55 % 73 50 % 80 45 % 8C 40 % 99 35 % A6 30 % B3 25 % BF 20 % CC 15 % D9 10 % E6 5 % F2 0 % FF

透明度%A的16进制数透明度%A的16进制数
100%0095%0D
90%1A85%26
80%3375%40
70%4D65%59
60%6655%73
50%8045%8C
40%9935%A6
30%B325%BF
20%CC15%D9
10%E65%F2
0%FF

请勿修改AndroidManifest中,首页Activity的label属性

因为修改后,你自个的APP名字也会跟着变 至于其他页面,则只会使Toolbar和ActionBar显示对应的label字符串

Navigation 组件使用入门(官方文档)

(链接复制时间:2022.6.15)

developer.android.google.cn/guide/navig…

Navigation配合导航栏使用(官方文档)

(链接复制时间:2022.6.15)

developer.android.google.cn/guide/navig…

当前大部分软件都是采用底部导航了,这有官方的BottomNavigationView

注意:BottomNavigationView与Navigation搭配使用还有一个问题,就是BottomNavigationView的点击Fragment切换使用的是Fragment.replace,所以每一次点都是重建的。

Navigation导航组件的三部分

  • 导航图
  • NavHost
  • NavController

导航图

在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为目标)以及用户可以通过应用获取的可能路径。

NavHost

显示导航图中目标的空白容器。导航组件包含一个默认 NavHost 实现 (NavHostFragment),可显示 Fragment 目标。

NavController

在 NavHost 中管理应用导航的对象。当用户在整个应用中移动时,NavController 会安排 NavHost 中目标内容的交换。

Kotlin 变量

Kotlin的变量并不需要像Java一样在前面定义这个变量的类型,而是只需要在变量面前定义var 或val。

val (value) 声明这是一个不可变的变量,赋值后无法再修改他的值,相当于Java fianl变量

var (variable) 声明一个可变的变量,复制后可以被修改

自动推导机制:在未显式的声明一个变量,在赋值时,Kotlin自动推导出他是什么类型的变量

显性赋值变量 val a : Int = 10

Kotlin语法糖

当函数(方法)中只有一行代码的时候,Kotlin允许我们不写函数体(方法体),直接写在函数的尾部

//正常写法
fun getMaxNumber(num1 : Int, num2 : Int) : Int{
    return max(num1, num2)
}
//语法糖写法
fun getMaxNumber(num1 : Int, num2 : Int) = max(num1, num2)

Kotlin - if条件语句

fun getMaxNumber(num1 : Int, num2 : Int) : Int {
    var max = 0
    if(num1 > num2){
        max = num1
    } else{
        max = num2
    }
    return max
}
//配合语法糖后的结果
fun getMaxNumber(num1: Int, num2: Int) = if (num1 > num2) num1 else num2

kotlin when条件语句

fun getScore(score : String) : String{
    var sc = ""
    sc = when(score){
        "SSS" -> "你真是个天才"
        "SS" -> "非常的不错"
        "S" -> "这次成绩不错"
        "A" -> "下次需要努力了哦"
        "B" -> "还得再加把劲"
        "C" -> "要在努努力,老师给你开小灶"
        else -> "问题不大,老师专职教导"
    }
    return sc
}

fun getScore(score: String) = when (score) {
    "SSS" -> "你真是个天才"
    "SS" -> "非常的不错"
    "S" -> "这次成绩不错"
    "A" -> "下次需要努力了哦"
    "B" -> "还得再加把劲"
    "C" -> "要在努努力,老师给你开小灶"
    else -> "问题不大,老师专职教导"
}

kotlin while循环

fun getSum() : Int{
    var x = 10
    var sum = 0
    while(x > 0){
        sum += x
        x--
    }
    return sum
}

Kotlin 区间

var range = 0..10 0到10的闭合区间 [0,10]

var range = 0 until 10 0到9的 左闭右开区间 [0, 10)

Kotlin for-in 循环

//循环显示当前数值
private fun printlnNum(){
    for(i in 1..10){
        println(i)
    }
}
//结果输出:0 1 2 3 4 5 6 7 8 9 10

step关键字,可以跳过一些元素

private fun printlnNum(){
    for(i in 0 until 10 step 2){
        println(i)
    }
}
//结果输出:0 2 4 6 8

downTo 降序

private fun printlnNum(){
    for(i in 10 downTo  1 step 2){
        println(i)
    }
}
//输出结果: 10 8 6 4 2

面向对象编程

//略

类与对象

Kotlin使用class关键字来声明一个类 声明一个类取消了Java的new 而获取与设置某个属性也只需要实例对象.属性,而不需要使用set()和get()

//Kotlin
val person = Person()
person.age = 10
val sex = person.sex
//Java
Person person = new Person();
person.setAge(10);
String sex = person.getSex();

Kotlin继承

Kotlin的类默认都是不允许继承的,若要他被继承,则必须要在他面前增加 open关键,且继承使用的关键字是:冒号,而不是Java中的extends。继承的括号请看下面

open class Person{
//...
}

class Student : Person() {
//...
}

继承时的括号为构造函数 Kotlin的构造函数有:主构造函数和次构造函数 每一个类都默认有一个无参的主构造函数,而主构造函数是你最常用的构造函数,主构造函数是无函数体的,但你想要在主函数体重实现一些逻辑,可以使用init{}

class Student(val id: Int, val grade : Int) : Person() {
    init{
        println("student id is :" + id)
    }
}

val student = Student(1001, 99)

继承类需要调用父类的构造函数,所以Person()括号就是调用父类的构造函数,无参构造时,也不能省略括号

open class Person (val name: String, val age : Int) {
//...
}

class Student(val id: Int, val grade : Int, name : String, age : Int) : Person(name, age) {
}

val student = Student(1001, 99, "XiaoMing", 19)

Kotlin次构造函数

当有主构造函数和次构造函数时,次构造函数必须调用主构造函数(间接或直接)

class Student(val id: Int, val grade : Int) : Person(name, age) {
    constructor(name: String, age: Int) : this(1001, 99, name, age){
    }
    constructor() : this("", 0){
    
    }
}

val student = Student(1001, 99, "XiaoMing", 19)

函数的参数默认值

text2(1001) //正常使用
//introduce 默认显示"本人很酷",可以不传
private fun text2(id : Int, introduce : String = "本人很酷~"){
}

text2("小明") //无法使用
text2(name = "小明") //能正常使用,自己填写参数名name = “你的名字”
//introduce 默认显示"本人很酷",可以不传
private fun text2(id : Int, name : String){
}

次构造函数为何不常用

正因为上面的参数默认值的原因,导致一个主构造函数可以变成多个构造函数使用,所以次构造函数才很少用

这样我就可以使用多种方式进行创建实例
val student = Student(name = "小明")
val student = Student(name = "小明", sex = "男")
class Student(name : String = "", sex : String = "保密" , id : Int = 10000, age : Int = 0){

}

Kotlin 接口

通过:(和继承一样),一个继承和多个接口之间用逗号 , 分开

interface Study{
    fun readBooks() //可以不实现函数体
    fun doHomework(){ //可以进行默认实现
        //.....
    }
}

class Student() : Person(), Study{
    override fun readBoos(){ //必须重写,否则报错
        //...
    }
    //doHomework函数不是必须实现,若不重写则进行默认的函数体
}

修饰符

Java

  • public 公开
  • private 私有
  • protected 保护
  • default (默认) Kotlin
  • public 公开 (默认情况)
  • private 私有
  • protected 保护(只允许当前类和它的子类可以看得到)
  • internal 同一模块可用,其他模块不可用 此图来源于第一行代码,第三版

image.png

Kotlin 数据类

data关键字可以省却重写equals()、hashCode()、toString()等方法

data class Student(val name: String, val sex : String)

Kotlin 单例类

将class关键字改成object既可

object SingleTest{
    fun singlePrintln(){
        println("single")
    }
}

//使用
SingleTest.singlePrintln()

Kotlin 集合

List

val list = ArrayList<String>()
list.add("AAA")
list.add("BBB")
list.add("CCC")
list.add("DDD")
//listOf()简化写法,但其创建的是不可修改的集合
val list = listOf("AAA", "BBB", "CCC", "DDD")
//可改变的集合
val list = mutableListOf("AAA", "BBB", "CCC", "DDD")

Set

Set和List差不多,只是listOf()变成setOf(), mutableListOf()变成mutableSetOf()

Map

Map是以键值对的形式存储的数据结果

val map = HashMap<String, Int>()
map["AAA"] = 1
map["BBB"] = 2
map["CCC"] = 3

val map = mapOf("AAA", "BBB", "CCC")
val map = mutableMapOf("AAA", "BBB", "CCC")

集合的API

val list = listOf("Apple", "Banana", "Pear", "Watermelon")
//获取最大长度的水果
val maxLength = list.maxByOrNull{it.length}
//获取一个新集合,字母全变成大写
val newList = list.map{it.uppercase()}
//获取过滤后的集合(此处是长度小于等于5的字符串)
val newList = list.filter{it.length <= 5}
//any判断是否有一个或以上的元素满足条件,返回布尔值
val anyResult = list.any{it.length <=5}
//all判断是否所有元素都满足条件,返回布尔值
val allResult = list.all{it.length <=5}

Kotlin代码调用Java的API

可以调用,但有条件。 如果在Kotlin中调用的Java方法,并且接受一个Java单抽象方法接口参数,就可以使用函数式API

//Java写法
new Thread(new Runnable() {
    @Override
    public void run() {

    }
}).start();

//Kotlin写法
Thread(object : Runnable{
    override fun run() {
        println("XXX")
    }
}).start()

//函数式API精简
Thread(Runnable { 
    println("XXX")
}).start()

//Kotlin特性缩减
Thread {
    println("XXX")
}.start()

//同理:点击事件
button.setOnClickListener {
    //监听代码
}

空指针

在Kotlin中,存在空指针判空机制,编译时就会进行判空检查

//正常情况下
private fun printlnStudent(student: Student) {
    if(student != null) {
        student.doHomework()
    }
}
//有判空机制后,因为他本身就不允许传入空,在传入的时候就会报错
private fun printlnStudent(student: Student) {
    student.doHomework()
}

printlnStudent(null) //直接编译报错

在变量后面加个问号 ? ,该变量就允许传入空的变量

但可以传空后,里面的方法可以能会报错,这时就可以加 问号加点 ?. 判空操作符

//变成 Student? 后,可以传入空变量,也就成了,下面的方法可能会报错
private fun printlnStudent(student: Student?) {
    student?.doHomework()  //当student不为空时,才会执行doHomework()函数
}

?: 操作符

判空表达式,若不为空,就返回 a变量,若是空,返回 b 数据 a ?: b

//正常if
private fun text(a : String?) {
    val b = "ABC"
    val c = if(a != null){
        a
    } else{
        b
    }
}
//使用?. 操作符
private fun text(a : String?) {
    val b = "ABC"
    val c = a ?: b
}

笨比的Kotlin判空机制

虽然你在外面进行了判空处理,它依然不知道你判空了,所以,哪怕外面你使用if(a !=null)后进行方法的执行,但他方法内还是会报错。 这是就可以使用!!. 操作符来进行屏蔽判空处理,告诉Kotlin这个笨比小弟,大哥我百分之两百不会空,不需要你判断,揍开! 当然,最好这样可能不太好,比较可能存在空引用。

var a : String? = "ABCdef" //全局变量
private fun text() {
    val b = a.uppercase() //报错
    val c = a!!.uppercase() //正常
}

字符串内嵌表达式

val name = "小明"
val str = "你好, $name + 同学,欢迎来到这里~"

标准函数

标准函数是指Standard.kt中定义的函数,所有地方都可以使用 let函数、with函数、run函数、apply函数都是标准函数

let函数

主要作用是配合?. 操作符辅助判空处理 调用let函数既可以执行里面代码,里面的it对象,就是student,同时可以搭配 ?. 一起使用,用法比上面的写法更简洁,不空时,才会执行里面的代码

private fun text(student: Student?) {
    student?.doHomework()
    student?.readBoos()
    
    student?.let { obj -> 
        obj.doHomework()
        obj.readBoos()
    }
    //仅有一个参数,可以用it代替
    student?.let { 
        it.doHomework()
        it.readBoos()
    }
}

with函数

接受两个参数

  • 任意类型的对象
  • Lambda表达式(对象为第一个参数) 作用:使连续调用同一个对象的多个方法变得更简洁,最后一行作为返回值
private fun text2(): String {
    //正常写法
    val list = mutableListOf("AAA", "BBB", "CCC", "DDD")
    val str = StringBuilder()
    str.append("开始加数据:")
    for (x in list) {
        str.append(x)
    }
    str.append("结束")
    val result = str.toString()
    return result
    
    //with写法
    val result = with(str) {
        append("开始加数据:")
        for (x in list) {
            append(x)
        }
        append("结束")
        toString()
    }
    return result
}

run 函数

run与with差不多,但并不是直接with,而是实例对象.run{} 同样返回最后一行的值

//run写法
val result = StringBuilder().run {
    append("开始加数据:")
    for (x in list) {
        append(x)
    }
    append("结束")
    toString()
}
return result

apply函数

与run函数类似,但返回的是对象本身

//apply写法
val result = StringBuilder().apply {
    append("开始加数据:")
    for (x in list) {
        append(x)
    }
    append("结束")
}
return result.toString()

静态方法

Kotlin弱化静态方法吗,而单例比静态方法更好使用 单例类会将所有方法都变成像静态方法的调用方式 但只是想有一两个函数变成静态方法。请使用companion object关键字 真正的静态方法请使用注解 @JvmStatic,且该注解只能在companion object中使用

//单例类
object UiUtil {
    fun test(){
        println("XXX")
    }
}
//使用方式
UiUtil.test()

//正常类
class UiUtil {
    companion object{
        fun test(){
            println("XXX")
        }

        @JvmStatic
        fun test2(){
            println("XXX")
        }
    }
}

顶层方法

定义在类以外的地方,不定义在类里面 可以创建一个File,里面不创建类,就只单纯的写函数(里面的函数会被编译成静态函数) 同时,里面的方法在其他地方都可以直接doHomework()使用,无需加包名,无需实例化,直接用 同时在Java中也能使用这些顶层函数,但需要.方法() - 》 Hepler.doHomework()

//整个文件
package com.text.applicationApp

fun doHomework(){
    println("XXX")
}

变脸延迟初始化

使用关键字 lateinit,我们不再需要定义变量是赋值null !:: 操作符可以判断该变量是否进行初始化操作

class MainActivity : AppCompatActivity() {
    private lateinit var student: Student
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        student = Student(1001, 96)
        //因为student必定不为空(没有?),所以不会报错,但这两行代码若换过来执行,会报错,所以,必须要争实例化对象后再使用
        student.doHomework() 
        
        //若初始化才执行
        if(!::student.isInitialized){
            student.doHomework() 
        }
        
    }
}
class Student(val id: Int, val grade : Int) {
    fun doHomework(){
        println("doHomework")
    }
}

只能修饰非空变量 :  不能修饰可空变量 , 否则报错 只能修饰引用数据类型 :  不能修饰 8 88 种基本数据类型 , 否则报错

密封类

关键字sealed sealed class Result 密封类会强制要求你的子类所有条件都进行处理

//正常使用法
interface Result
class Success(val msg : String) : Result
class Failure(val error : Exception) : Result
//虽然不可能进入else,但不写的话,就会编译报错
fun getResultMsg(result : Result) = when (result){
    is Success -> result.msg
    is Failure -> result.error.message
    else -> throw IllegalArgumentException()
}

//密封类的写法
sealed class Result
class Success(val msg : String) : Result()
class Failure(val error : Exception) : Result()
fun getResultMsg(result: Result) = when (result){
    //这里因为Result类的子类只有两个,所以这里也必须两个子类都实现了,否则编译会报错
    //若增加一个子类后,这里也会直接报错,告诉你,还少了一个
    is Success -> result.msg
    is Failure -> result.error.message
}

Kotlin 扩展函数

(Kotlin也有一些自带的扩展函数) 在不修改某个类的情况下,向这个类添加一个新的函数 扩展函数的结构

fun ClassName.methodName(param1: Int, param2: Int) : Int{
    return 0
}

例:判断一个字符串有多少个字母

//日常写法
object UiUtil {
    fun lettersCount(str : String) : Int{
        var count = 0
        for(char in str){
            //判断该字符是不是字母
            if(char.isLetter()){
                count++
            }
        }
        return count
    }
}

//String扩展函数
fun String.lettersCount() : Int{
    var count = 0
    for(char in this){
        //判断该字符是不是字母
        if(char.isLetter()){
            count++
        }
    }
    return count
}
//使用方式
str.lettersCount()

运算符重载

运算符重载的关键字:operator 系统的运算法只能用于基本数据类型的计算 而对象的加减乘除是不一定有意义的,两个"苹果"类加减?没用。两个"钱"类加减?可能有用 这时就用到了运算符重载 语法结构

class Obj {
    operator fun plus(obj: Obj) : Object{
        //进行你要的加法逻辑
    }
}

val obj1 = Obj()
val obj2 = Obj()
val obj3 = obj1 + obj2
在方法里面写你想要的逻辑,同时返回的Object对象是你自己觉得是什么,可以是Obj,也可以是Int等等

同时,同一个运算符可以有多重重载,也就是多个plus(加法)

所有运算符的函数名,同时他的语法糖表达式

语法糖表达式实际调用函数
a + ba.plus(b)
a - ba.minus(b)
a * ba.times(b)
a / ba.div(b)
a % ba.rem(b)
a++a.inc()
a--a.dec()
+aa.unaryPlus()
-aa.unaryMinus()
!aa.not()
a == ba.equals(b)
a > b
a < ba.compareTo(b)
a >= b
a <= b
a..ba.rangeTo(b)
a[b]a.get(b)
a[b] = ca.set(b, c )
a in bb.contains(a)

高阶函数

一个函数接受另一个函数作为参数,或者返回值的类型是另一个参数,那么该函数就称之为高阶函数

作为参数的函数类型格式 : (String, Int) -> Unit

左边是函数传入的参数,右边是他的返回值,不返回内容就Unit

同一个高阶函数,因为传入的函数类型不通,结果往往也是截然不同的

fun StringBuilder.build(block: StringBuilder.() -> Unit) : StringBuilder{
    block()
    return this;
}
val list = listOf("Apple", "Banana", "Pear", "Watermelon")
val result = StringBuilder().build {
    append("Start")
    for (fruit in list) {
        append(fruit)
    }
    append("End")
}
println(result.toString())

内联函数

在高阶函数上加 inline关键字既可 Kotlin编译时,会将内联函数的代码,自动替换到其他地方,不会造成运行时的开销

noinline 与 crossinline

在内联函数中,有两个函数类型的参数,只希望有一个不进行内联,这是可以用到noinline 内联的好处:减少运行时的内存开销,但他编译时会替换代码,没有真正的属性,而非内联的参数可以自由传递给其他函数,内联的函数参数类型只允许传递给另一个内联函数。

inline fun inlineTest(test1 : () -> Unit, noinline test2 : () -> Unit){
    
}

泛型

泛型允许我们在不指定具体的类型的情况下进行编程。 泛型有两种:第一种是泛型类,第二种是泛型方法

class MyClass<T> {
    fun method(param: T) : T{
        return param
    }
}

//正常类中的泛型方法
class MyClass {
    fun <T> method(param: T) : T{
        return param
    }
}

泛型类还能指定任意的类型

//Number类有:Int、Float、Double、Long等
class MyClass {
    fun <T : Number> method(param: T) : T{
        return param
    }
}

泛型默认的是Any?所以,他是可空的,若不想其可空,可以使用Any 定义一个全局的build扩展高阶函数,任何类都可以用

fun <T> T.build(block : T.() -> Unit) : T{
    block()
    return this
}


val i = 10
i.build { 
    //.....
}

委托

委托是一种设计模式,基本理念:操作对象自己不会去处理某段逻辑,而是会把工作委托给另一个辅助对象去处理。 委托又分为:类委托 和 委托属性

委托模式:主要作用是,可以让我们大部分的方法直接调用辅助对象中的方法,而少数几个我们自己进行重写。还可以增加自己的方法

类委托

核心思想:将一个类的具体实现委托给另一个类去实现 关键字 by

class MySet<T> (private val helperSet: HashSet<T>) : Set<T> by helperSet{
//在未使用by helperSet之前,下面的方法是需要重写的,而使用后,就大大减少了无用的代码重写
//我们只需要对需要的方法单独重写既可与增加自己想要增加的方法
      fun helloWorld() = println(Hello World)
      override fun isEmpty() = false
      
//    override val size: Int
//        get() = helperSet.size
//
//    override fun contains(element: T): Boolean {
//        return helperSet.contains(element)
//    }
//
//    override fun containsAll(elements: Collection<T>): Boolean {
//        return helperSet.containsAll(elements)
//    }
//
//    override fun isEmpty(): Boolean {
//        return helperSet.isEmpty()
//    }
//
//    override fun iterator(): Iterator<T> {
//        return helperSet.iterator()
//    }

}

委托属性

核心思想,将一个属性(字段)的具体实现委托给另一个类去完成

class MyClass {
    //用法
    var p by Delegate()
}

class Delegate {
    private var propValue: Any? = null
    //第一个参数:只有在MyClass中可以使用, 第二个参数,KProperty<*>是Kotlin中的一个属性操作类,可以用于获取各种属性相关的值
    operator fun getValue(myClass: MyClass, prop: KProperty<*>): Any? {
        return propValue
    }
    //当上面使用的是val 而不是var时,是可以不实现该方法,
    operator fun setValue(myClass: MyClass, prop: KProperty<*>, value: Any?) {
        propValue = value
    }

}

<*>

此类型的泛型表示:你不知道或者你关心泛型的具体类型

by lazy懒加载

当我们将想要延迟执行的代码放入by lazy代码块中,他不会类被实例化的时候就执行,而是到了第一次执行时才会被调用

class MyClass {
    val p by lazy {
        Delegate()
    }
}

infix函数

可以将一个函数使用特殊的语法糖格式,变得更加精简 但有两个严格的限制:infix函数不能定义成顶层函数,必须是某个类的成员函数,可以通过扩展函数吧他定义到某个类中,第二点,他只接受一个参数,但参数类型没有限制

if("Hello World" beginsWith "Hello"){
    //...
}

infix fun String.beginsWith(str: String) = startsWith(str)

泛型实化

Kotlin 的内联函数在编译时,会将代码自动调到使用的地方,所以在编译后,泛型,其实就是实际的数据类型 这也就意味着,内联函数的泛型是可以进行实化的。切必须是内联函数。且在泛型前面加上reified关键字表示要泛型实化

//使用方法
val result = getGenericType2<Int>()
//泛型实化方法
inline fun <reified T> getGenericType2() = T::class.java

泛型实化的应用

//正常用法
val intent = Intent(context, TestActivity::class.java)
context.startActivity(intent){
    //putExtra("text", text)
}
//泛型实化用法
startActivity<TestActivity>()
inline fun <reified  T> startActivity(context : Contextblock : Intent.() -> Unit){
    val intent = Intent(context, T::class.java)
    context.startActivity(intent)
}

泛型的协变与逆变

泛型传入与传出的两个地方,传入的地方被称为in位置,传出的地方被称为out位置**(重要)** fun method(param: T(in)) : T (out)

协变: 假定一个MyClass<T>泛型类,其中A是B的子类,同时MyClass<A>又是MyClass<B>的子类,那么MyClass在T这个泛型上是协变的 想要MyClass<A>是MyClass<B>的子类,那么久只需要让MyClass<>所有方法都不能接受T类型的参数,T只能出现在out位置上,不能出现在in位置上

class MyClass<out T>(val data : T?){
    fun get() : T? {
        return data
    }

这时候,你就可以将你的MyClass<A> 的对象传到接受MyClass<B>的方法中

List本身只可读,MutableList才是可以写入,所以List本身就是可以协变

image.png (你会发现contains()方法的E出现在in位置上,这是因为前面加了一个@UnsafeVariance注解,这样就可以允许在in位置上了,但这个方法的目的就是判断当前集合中是否包含传入的这个元素,不会修改List,所以是安全的)

逆变 (典型的逆变例子:Comparable接口) 逆变和协变是完全相反的,假设定义了一个MyClass<T>泛型类,A是B的子类,同时MyClass<B> 又是MyClass<A>的子类,那么我们可以称MyClass在T这个泛型上是逆变的 A 是B 子类 ,MyClass<B>是 MyClass<A>子类????

fun main() {
    val trans = object : Transformer<Person> {
        override fun transform(t: Person): String {
            return "${t.name} ${t.age}"
        }
    }
    handleTransformer(trans)
}

fun handleTransformer(trans: Transformer<Student>) {
    val student = Student("Tom", 19)
    val result = trans.transform(student)
}

//在此处使用了in T,表明,只能在in位置上使用T,不能再out位置上使用,这也就是泛型T的逆变
interface Transformer<in T> {
    fun transform(t: T): String
}

open class Person(val name: String, val age: Int) {

}

class Student(name: String, age: Int) : Person(name, age) {

}

//下面用于copy