我:它用起来也简单,想控制什么,把对应的泛型传进去就行,就跟选模式一样:
// 电视机作为泛型实参
// ↓
val tvController: Controller = Controller()
val tv = TV()
// 控制电视机
tvController.turnOn(tv)
tvController.turnOff(tv)
// 电风扇作为泛型实参
// ↓
val fanController: Controller = Controller()
val fan = Fan()
// 控制电风扇
fanController.turnOn(fan)
fanController.turnOff(fan)
借助 Kotlin 的顶层函数,Controller 类甚至都可以省掉,直接用泛型函数:
1-2 泛型函数
// 函数的泛型参数
// ↓ ↓
fun turnOn(obj: T){ … }
fun turnOff(obj: T){ … }
泛型函数用起来也简单:
// 控制电视
val tv = TV()
turnOn(tv)
turnOff(tv)
// 控制风扇
val fan = Fan()
turnOn(fan)
turnOff(fan)
女朋友:我知道怎么用啦!是不是这样?
val boyFriend = BoyFriend()
turnOff(boyFriend)
我:……
2. 招聘的故事:泛型的不变性(Invariant)
女朋友:我想招几个大学生做兼职,你推荐几个大学吧。
我:好嘞,不过我要通过 Kotlin 泛型来给你推荐。
女朋友:呃……刚才你讲的泛型还挺简单,这次有什么新花样吗?
我:你看下去就知道了。
我:先来点准备工作:
// 学生
open class Student()
// 女学生
class FemaleStudent: Student()
// 大学
class University(val name: String) {
// 往外取,代表招聘
fun get(): T { … }
fun put(student: T){ … }
}
我:你的招聘需求可以用这样的代码描述:
// 注意这里
// 女朋友需要一个大学(变量声明) ↓
lateinit var university: University
// 注意这里
// 我随便推荐一个大学 ↓
university = University(“某大学”)
val student: Student = university.get()// 招聘
女朋友:原来 Kotlin 也没那么难……
女朋友:能赋值一个"女子大学"吗?
我:不行,会报错。
// 注意这里
// ↓
lateinit var university: University
// 这是报错的原因
// ↓
university = University(“女子大学”)
val student: Student = university.get()
// 编译器报错!!
/*
Type mismatch.
Required: University
Found: University
*/
女朋友:什么鬼。。。
我:虽然 Student 和 FemaleStudent 之间是父子关系,但是 University 和 University 之间没有任何关系。这叫泛型的不变性。
女朋友:这不合理!女子大学招聘出来的学生,难道就不是学生?
我:招聘当然符合逻辑,但别忘了 University 还有一个 put 方法。
我:你怎么防止别人把一个男学生放到女子大学里去?
我:让我们看看如果可以将“女子大学”当作“普通大学”用,会出现什么问题:
// 声明的类型是:普通大学,然而,实际类型是:女子大学。
// ↓ ↓
var university: University = University(“女子大学”)
val maleStudent: Student = Student()
// 男学生被放进女子大学!不合理。
university.put(maleStudent)
女朋友:明白了,原来这就是泛型不变性的原因,确实能避免不少麻烦。
// 默认情况下,编译器只允许这么做
// 声明的泛型参数与实际的要一致
↓ ↓
var normalUniversity: University = University
↓ ↓
var wUniversity: University = University
3. 搞定招聘:泛型的协变(Covariant)
女朋友:如果我把 University 类里面的 put 方法删掉,是不是就可以用“女子大学”赋值了?这样就不用担心
把男学生放到女子大学
的问题了。我:这还不够,还需要加一个关键字
out
告诉编译器:我们只会从 University 类往外取,不会往里面放。这时候,University 就可以当作 University 的子类。我:这叫做泛型的
协变
。
open class Student()
class FemaleStudent: Student()
// 看这里
// ↓
class University(val name: String) {
fun get(): T { … }
}
女朋友:我试试,果然好了!
// 不再报错
var university: University = University(“女子大学”)
val student: Student = university.get()
我:你不来写代码真浪费了。
4. 填志愿的故事:泛型的逆变(Contravariant)
女朋友:我妹妹刚高考完,马上要填志愿了,你给推荐个大学吧。
我:咱刚看过泛型协变,要不你试试自己解决这个填志愿的问题?正好 University 里有个 put 方法,你就把 put 当作填志愿就行了。
女朋友:那我依葫芦画瓢试试…… 给我妹妹报一个女子大学。
open class Student()
class FemaleStudent: Student()
class University(val name: String) {
fun get(): T { … }
// 往里放,代表填志愿
fun put(student: T){ … }
}
val sister: FemaleStudent = FemaleStudent()
val university: University = University(“女子大学”)
university.put(sister)//填报女子大学
女朋友:完美!
我:厉害。
女朋友:能不能再报一个普通综合大学?
我:不行,你忘记泛型不变性了吗?
val sister: FemaleStudent = FemaleStudent()
// 报错原因:声明类型是:女子大学 赋值的类型是:普通大学
// ↓ ↓
val university: University = University(“普通大学”)
university.put(sister)
// 报错
/*
Type mismatch.
Required: University
Found: University
*/
女朋友:我妹能报女子大学,居然不能报普通的综合大学?这不合理吧!
我:你别忘了 University 还有一个 get 方法吗?普通综合大学 get 出来的可不一定是女学生。
女朋友:哦。那我把 get 方法删了,再加个关键字?
我:对。删掉 get 方法,再加一个关键字:
in
就行了。它的作用是告诉编译器:我们只会往 University 类里放,不会往外取。这时候,University 就可以当作 University 的子类。我:这其实就叫做泛型的
逆变
,它们的继承关系反过来了。
// 看这里
// ↓
class University(val name: String) {
fun put(student: T){ … }
}
val sister: FemaleStudent = FemaleStudent()
// 编译通过
val university: University = University(“普通大学”)
university.put(sister)
女朋友:泛型还挺有意思。
我:上面提到的
协变
和逆变
。它们都是通过修改 University 类的泛型声明
实现的,所以它们统称为:声明处型变
,这是 Kotlin 才有的概念,Java 中没有。
5. 使用处型变(Use-site Variance)
女朋友:万一 University 是第三方提供的,我们无法修改,怎么办?能不能在不修改 University 类的前提下实现同样的目的?
我:可以,这就要用到
使用处型变
了。他们也分为:使用处协变
,使用处逆变
。
open class Student()
class FemaleStudent: Student()
// 假设 University 无法修改
class University(val name: String) {
fun get(): T { … }
fun put(student: T){ … }
}
5-1 使用处协变
我:在泛型的
实参
前面增加一个out
关键字,代表我们只会从 University 往外取,不会往里放。这么做就实现了使用处协变
。
// 看这里
// ↓
fun useSiteCovariant(university: University) {
val femaleStudent: Student? = university.get()
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
总结
可以看出,笔者的工作学习模式便是由以下 「六个要点」 组成:
❝ 多层次的工作/学习计划 + 番茄工作法 + 定额工作法 + 批处理 + 多任务并行 + 图层工作法❞
希望大家能将这些要点融入自己的工作学习当中,我相信一定会工作与学习地更富有成效。
下面是我学习用到的一些书籍学习导图,以及系统的学习资料。每一个知识点,都有对应的导图,学习的资料,视频,面试题目。
**如:我需要学习 **Flutter的知识。(大家可以参考我的学习方法)
- Flutter 的思维导图(无论学习什么,有学习路线都会事半功倍)
- Flutter进阶学习全套手册
- Flutter进阶学习全套视频
大概就上面这几个步骤,这样学习不仅高效,而且能系统的学习新的知识。
识点,都有对应的导图,学习的资料,视频,面试题目。
**如:我需要学习 **Flutter的知识。(大家可以参考我的学习方法)
- Flutter 的思维导图(无论学习什么,有学习路线都会事半功倍)
[外链图片转存中…(img-IQdFUSFT-1711626628007)]
- Flutter进阶学习全套手册
[外链图片转存中…(img-KaFZLjYE-1711626628007)]
- Flutter进阶学习全套视频
[外链图片转存中…(img-Wy3QsquU-1711626628008)]
大概就上面这几个步骤,这样学习不仅高效,而且能系统的学习新的知识。