面向对象的三大特征
- 多态
- 封装
- 继承
面向对象的五大基本原则
- 单一职责原则
- 开放封闭原则
- 里氏替换原则
- 接口隔离原则
- 依赖倒置原则
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% | 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 |
请勿修改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 同一模块可用,其他模块不可用 此图来源于第一行代码,第三版
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 + b | a.plus(b) |
| a - b | a.minus(b) |
| a * b | a.times(b) |
| a / b | a.div(b) |
| a % b | a.rem(b) |
| a++ | a.inc() |
| a-- | a.dec() |
| +a | a.unaryPlus() |
| -a | a.unaryMinus() |
| !a | a.not() |
| a == b | a.equals(b) |
| a > b | |
| a < b | a.compareTo(b) |
| a >= b | |
| a <= b | |
| a..b | a.rangeTo(b) |
| a[b] | a.get(b) |
| a[b] = c | a.set(b, c ) |
| a in b | b.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 : Context, block : 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本身就是可以协变
(你会发现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