}
void set(Object t){
this.t = t;
}
}
那么既然运行的时候,泛型限制全都没有了。那么怎么保证 泛型的作用呢?
答案:编码的时候,编译器帮我们进行校验。
```java
strList.add(123);//报错
PECS 法则 和 上下边界的问题
public class Panzi<T> {
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<Apple> applePanzi = new Panzi<>(new Apple());
Panzi<Fruit> fruitPanzi = new Panzi<>(new Fruit());
fruitPanzi = applePanzi;
虽然 Apple和Fruit是父子继承关系。但是Panzi<Apple>
和 Panzi<Fruit>
是半毛钱关系都没有,如果你想fruitPanzi = applePanzi
,把后者赋值给前者,是会报编译错误的。
如果想让装水果的盘子和 装 苹果的盘子发生一点关系,能够后者赋值给前者.
Panzi<Apple> 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: T) {
var t: T = t
fun get(): T {
return t
}
fun set(t: T) {
this.t = t
}
}
fun test1() {
// 试试能不能相互赋值
var fruitPanzi: Panzi<Fruit> = Panzi(Fruit()) //一个水果盘子
var applePanzi: Panzi<Apple> = Panzi(Apple()) //一个苹果盘子
//试试相互赋值
// fruitPanzi = applePanzi // 编译报错
// applePanzi = fruitPanzi // 编译报错
//双方完全是不相干的类,不能相互赋值 ,
}
/**
* 加边界之后再赋值
*/
fun test2() {
//如果你非要认为苹果盘子归属于水果盘子,那么可以这样
var fruitPanzi2: Panzi<out Fruit> = Panzi(Fruit()) //一个水果盘子
var applePanzi2: Panzi<Apple> = Panzi(Apple()) //一个苹果盘子
fruitPanzi2 = applePanzi2 //那么这就是out决定泛型上边界的案例
}
/**
* PECS法则,OUT表示 ? extends 决定上界,上界生产者只取不存
*/
fun test3() {
//看一下get set方法
// 决定上界之后的泛型,只取不存
var fruitPanzi2: Panzi<out Fruit> = Panzi(Fruit())
fruitPanzi2.get()
// fruitPanzi2.set(Apple()) // 这里编译报错,和java泛型的表现一样
}
/**
* PECS法则,IN表示 ? super 决定下界,下界消费者,只存不取
*/
fun test4() {
//试试泛型下界 in
var fruitPanzi: Panzi<in Fruit> = 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<String>("str1", "str2", "str3")
listOf.forEach { print("$it ") }
}
执行结果:
str1 str2 str3
通过动态创建过程来指定元素值
fun main() {
val list = List(3) {
"str$it"
}
list.forEach { print("$it ") }
}
执行结果:
str0 str1 str2
api的调用
对象已经创建,我们要利用kotlin提供的方法来完成业务代码。
一级难度api(all,any,count,find,groupBy)
fun testCollectionFun() {
val ages = listOf<Int>(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
fun testCollectionFun2() {
//二级难度api
val ages = listOf<Int>(1, 2, 3, 4, 5, 6, 7, 100, 200)
// 只保留大于10的元素,并返回一个新数组
ages.filter { it > 10 }.apply { println(this) }
//遍历List的所有元素,根据条件返回值,创建新的元素内容并放到新List中返回出来
ages.map { if (it > 10) "大于10" else "小于等于10" }.apply { println(this) }
}
fun main() {
testCollectionFun2()
}
执行结果:
[100, 200]
[小于等于10, 小于等于10, 小于等于10, 小于等于10, 小于等于10, 小于等于10, 小于等于10, 大于10, 大于10]
- flatMap,因为稍复杂
// 比如一个学生,作为一个实体
class Student(name: String, math: Int, chinese: Int) {
val name: String = name
val score = Score(math, chinese)
}
//学生的成绩分数作为一个主体
class Score(math: Int, chinese: Int) {
val math: Int = math
val chinese: Int = chinese
}
fun testFlatMap() {
val students = listOf(
Student("zero", 100, 80),
### 最后
**一线互联网Android面试题含详解(初级到高级专题)**
> 这些题目是今年群友去腾讯、百度、小米、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率
![](https://img-blog.csdnimg.cn/img_convert/97b5d3afab5666bf9f1a5798e608c9c3.png)
> **[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**
**为什么分享?**
> 如果设置门槛,很多开发者朋友会因此错过这套高级架构资料,错过提升成为架构师的可能。这就失去了我们的初衷;让更多人都能通过高效高质量的学习,提升自己的技术和格局,升职加薪。
、新浪、搜狐等一线互联网公司面试被问到的题目。并且大多数都整理了答案,熟悉这些知识点会大大增加通过前两轮技术面试的几率
[外链图片转存中...(img-bMRFN9Yt-1630670536540)]
> **[CodeChina开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》](https://codechina.csdn.net/m0_60958482/android_p7)**
**为什么分享?**
> 如果设置门槛,很多开发者朋友会因此错过这套高级架构资料,错过提升成为架构师的可能。这就失去了我们的初衷;让更多人都能通过高效高质量的学习,提升自己的技术和格局,升职加薪。
最后送给大家一句话,望共勉,永远不要放弃自己的梦想和追求;