Kotlin(2)-泛型与集合(2)

类型擦除

我们在编码java的时候,写一个泛型类,可能是这样的

class Plate{
T t;

Plate(T t){
this.t = t;
}

T get(){
return t;
}

void set(T t){
this.t =
t;
}
}

以上是java代码。

Java泛型是伪泛型,在编译之后,所有的泛型写法都会被移除,而会用实际的类型去替换。

mian函数运行的时候, 被移除。而原来的T,就变成了Object。

所以,Plate的字节码反编译过来就应该是

class Plate{
Object t;

Plate(Object t){
this.t = t;
}

Object get(){
return t;
}

void set(Object t){
this.t = t;
}
}

那么既然运行的时候,泛型限制全都没有了。那么怎么保证 泛型的作用呢?

答案:编码的时候,编译器帮我们进行校验。

strList.add(123);//报错

PECS 法则 和 上下边界的问题

public class Panzi {
T mT;
public Panzi(T t) { mT = t;}
public T get() { return mT; }
public void set(T t) { mT = t; }
}

class Fruit {}
class Banana extends Fruit {}
class Apple extends Fruit {}

Panzi applePanzi = new Panzi<>(new Apple());
Panzi fruitPanzi = new Panzi<>(new Fruit());
fruitPanzi = applePanzi;

虽然 Apple和Fruit是父子继承关系。但是Panzi 和 Panzi是半毛钱关系都没有,如果你想fruitPanzi = applePanzi ,把后者赋值给前者,是会报编译错误的。

如果想让装水果的盘子和 装 苹果的盘子发生一点关系,能够后者赋值给前者.

Panzi applePanzi = new Panzi<>(new Apple());
Panzi<? extends Fruit> fruitPanzi = new Panzi<>(new Fruit());
fruitPanzi = applePanzi;

那就必须使用到上下边界的关键字 extends

extends 在泛型中表示指定上界,也就是说,实际类型都必须在Fruit之下(包括Fruit自己)。那么既然apple也是Fruit的子类,那么赋值就可以做到。

  • PECS法则

刚才说到了上边界 extends。而下边界是 super关键字

Panzi<? extends Fruit> extendsFruit = new Panzi<>(new Apple());
Panzi<? super Fruit> superFruit = new Panzi<>(new Fruit());

super关键字,在泛型中表示定义下界,实际类型必须在Fruit之上,同时也在Object之下(包括Fruit和Object)

所以会出现这么一个情况:

Panzi<? extends Fruit> extendsFruit = new Panzi<>(new Apple());
Panzi<? super Fruit> superFruit = new Panzi<>(new Object());

我们有这么两个Panzi,前者是 Fruit作为泛型上界,一个是Fruit作为下界。

现在,我们从Panzi中去调用get/set方法。会发现。

PE:

extendsFruit.set(new Apple()); // 编译报错!
Fruit fruit2 = extendsFruit.get();// 编译正常

为何?因为 Fruit作为上界,我get出来的类型可以确定一定是Fruit类型的。但是我set进去的时候,JVM无法判定实际类型(因为泛型被擦除,JVM只人为set(Object t) 的参数是一个Object),JVM要求是Object,但是你却给了一个Apple,编译器无法处理。所以干脆 java的泛型,? extends 定义了上界,只允许get,不允许set。这就是PECS中的PE,意思就是 Pruducer Extends ,生产者 Extends,只取不存。

相对应:

CS: 则是 Cunsumer super 消费者只存不取。

Object object = superFruit.get(); //get,get出来虽然不报错,但是没有任何意义。因为不能确定类型,只知道是一个Object,无法调用API
superFruit.set(new Fruit());// 但是set进去的时候,可以确定一定是一个Fruit的

这就是java泛型的 PECS法则.

kotlin泛型使用实例

java泛型里面比较纠结的难点就是类型擦除和PECS法则了。

那么kotlin泛型,原理上和java泛型和没有区别。只是写法上有了区别。

open class Fruit
class Apple : Fruit()
class Banana : Fruit()
class Panzi(t: T) {
var t: T = t
fun get(): T {
return t
}
fun set(t: T) {
this.t = t
}
}

fun test1() {
// 试试能不能相互赋值
var fruitPanzi: Panzi = Panzi(Fruit()) //一个水果盘子
var applePanzi: Panzi = Panzi(Apple()) //一个苹果盘子
//试试相互赋值
// fruitPanzi = applePanzi // 编译报错
// applePanzi = fruitPanzi // 编译报错
//双方完全是不相干的类,不能相互赋值 ,
}

/**

  • 加边界之后再赋值
    */
    fun test2() {
    //如果你非要认为苹果盘子归属于水果盘子,那么可以这样
    var fruitPanzi2: Panzi = Panzi(Fruit()) //一个水果盘子
    var applePanzi2: Panzi = Panzi(Apple()) //一个苹果盘子

fruitPanzi2 = applePanzi2 //那么这就是out决定泛型上边界的案例
}

/**

  • PECS法则,OUT表示 ? extends 决定上界,上界生产者只取不存
    */
    fun test3() {
    //看一下get set方法
    // 决定上界之后的泛型,只取不存
    var fruitPanzi2: Panzi = Panzi(Fruit())
    fruitPanzi2.get()
    // fruitPanzi2.set(Apple()) // 这里编译报错,和java泛型的表现一样
    }

/**

  • PECS法则,IN表示 ? super 决定下界,下界消费者,只存不取
    */
    fun test4() {
    //试试泛型下界 in
    var fruitPanzi: Panzi = Panzi(Fruit())
    fruitPanzi.set(Fruit())//可以set,但是看看get
    val get = fruitPanzi.get()//不会报错,get出来的类型就完全不能确定了,只知道是 顶级类Any? 的子类,获得它也没有意义

}


集合操作

Kotlin的集合,并没有重新开创一套规则,它的底层依然是java的Collection。Kotlin提供了可变集合和不可变集合的接口。

  • 不可变集合:List,Set,Map (内部元素不可以 增减 或者 修改,在定义的时候就已经将容量和内部元素定死)

  • 不可变集合: MutableList , MutableSet , MutableMap (声明的时候可以随意指定初始值,后续可以随意增删和修改内部元素)

集合操作分为:对象的创建api的调用

对象的创建

方式有多种,以不可变集合 List 为例,kotlin的List底层和Java的List一致,底层数据结构是数组。

静态指定元素值

fun main() {
val listOf = listOf(“str1”, “str2”, “str3”)
listOf.forEach { print("$it ") }
}

执行结果:

str1 str2 str3

通过动态创建过程来指定元素值

fun main() {

val list = List(3) {
"strKaTeX parse error: Expected 'EOF', got '}' at position 5: it" }̲ list.forEach {…it ") }
}

执行结果:

str0 str1 str2

api的调用

对象已经创建,我们要利用kotlin提供的方法来完成业务代码。

一级难度api(all,any,count,find,groupBy)

fun testCollectionFun() {
val ages = listOf(1, 2, 3, 4, 5, 6, 7, 100, 200)
//那么是不是所有的元素都大于10

ages.apply { print(“all:”) }.all { it > 10 }.apply { println(this) } //结果是false

//是不是存在任意一个元素大于10
ages.apply { print(“any:”) }.any { it > 10 }.apply { println(this) }

// 符合指定条件的元素个数
ages.apply { print(“count:”) }.count { it < 10 }.apply { println(this) }

//找到第一个符合条件的元素
ages.apply { print(“find:”) }.find { it > 10 }.apply { println(this) }

// 按照条件进行分组
val groupBy = ages.apply { print(“groupBy:”) }.groupBy { it > 10 }
groupBy[false].apply { println(this) }
groupBy[true].apply { println(this) }

}

fun main() {
testCollectionFun()
}

针对数组元素的简单判断,上述提供了简明的示例代码,用List为例,至于Set和Map类似。可以自主去推断写法。

执行结果:

all:false
any:true
count:7
find:100
groupBy:[1, 2, 3, 4, 5, 6, 7]
[100, 200]

二级难度api (filter,map,flatMap,flatten)
  • filter和map
《设计思想解读开源框架》

第一章、 热修复设计

  • 第一节、 AOT/JIT & dexopt 与 dex2oat

  • 第二节、 热修复设计之 CLASS_ISPREVERIFIED 问题

  • 第三节、热修复设计之热修复原理

  • 第四节、Tinker 的集成与使用(自动补丁包生成)

    第二章、 插件化框架设计

  • 第一节、 Class 文件与 Dex 文件的结构解读

  • 第二节、 Android 资源加载机制详解

  • 第三节、 四大组件调用原理

  • 第四节、 so 文件加载机制

  • 第五节、 Android 系统服务实现原理

    第三章、 组件化框架设计

  • 第一节、阿里巴巴开源路由框——ARouter 原理分析

  • 第二节、APT 编译时期自动生成代码&动态类加载

  • 第三节、 Java SPI 机制

  • 第四节、 AOP&IOC

  • 第五节、 手写组件化架构

    第四章、图片加载框架

  • 第一节、图片加载框架选型

  • 第二节、Glide 原理分析

  • 第三节、手写图片加载框架实战

    第五章、网络访问框架设计

  • 第一节、网络通信必备基础

  • 第二节、OkHttp 源码解读

  • 第三节、Retrofit 源码解析

    第六章、 RXJava 响应式编程框架设计

  • 第一节、链式调用

  • 第二节、 扩展的观察者模式

  • 第三节、事件变换设计

  • 第四节、Scheduler 线程控制

    第七章、 IOC 架构设计

  • 第一节、 依赖注入与控制反转

  • 第二节、ButterKnife 原理上篇、中篇、下篇

  • 第三节、Dagger 架构设计核心解密

    第八章、 Android 架构组件 Jetpack

  • 第一节、 LiveData 原理

  • 第二节、 Navigation 如何解决 tabLayout 问题

  • 第三节、 ViewModel 如何感知 View 生命周期及内核原理

  • 第四节、 Room 架构方式方法

  • 第五节、 dataBinding 为什么能够支持 MVVM

  • 第六节、 WorkManager 内核揭秘

  • 第七节、 Lifecycles 生命周期


    本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

    《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
    t 问题**

  • 第三节、 ViewModel 如何感知 View 生命周期及内核原理

  • 第四节、 Room 架构方式方法

  • 第五节、 dataBinding 为什么能够支持 MVVM

  • 第六节、 WorkManager 内核揭秘

  • 第七节、 Lifecycles 生命周期

    [外链图片转存中…(img-WGIlHmlu-1714764982736)]
    本文包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…
    [外链图片转存中…(img-19E4ACW1-1714764982738)]
    《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 22
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值