# 1. 泛型
**泛型**是一种编译时的安全检测机制,它允许在定义类、接口、方法时使用类型参数,声明的类型参数在使用时用具体的类型来替换。泛型的**本质是参数化类型**,也就是说所操作的数据类型被指定为一个参数。其实在之前的小节中也用到过泛型,且其用法和`Java`中基本一致,所以这里就简略介绍。
# 2. 泛型类
**泛型符号**可以是满足`Kotlin`命名规则的任意字符,甚至可以是某一个单词。比如下面的案例:
~~~
data class KotlinDemo<N, A>(var name: N, var age: A) {
override fun toString(): String {
return "KotlinDemo(name=$name, age=$age)"
}
}
fun main() {
var demo = KotlinDemo<String, Int>("张三", 24)
println(demo)
}
~~~
结果:
```
KotlinDemo(name=张三, age=24)
```
至于用在方法和接口上的使用类似,这里就不再继续给出案例。
# 3. 泛型约束
注意到上面的方式仅仅只是简单的应用,而实际上还有泛型约束。比如在`Java`中提供了`super`和`extends`关键字来进行。在`Kotlin`中类似的可以进行对类型的约束。
- `<? extends类或接口> ` 类比于: `<T:类或接口>`。可以理解为泛型的上界,因为在`Kotlin`中使用`:`来表示继承关系。
比如下面的案例:
~~~
open class Animal{
var weight = 30
}
// 继承
class Cat: Animal() {
var name = "Cat"
override fun toString(): String {
return "Cat(name='$name', weight=$weight)"
}
}
class KotlinDemo<T: Animal> (animal: T){
private var demo: T? = null
init {
if(this.demo == null) {
this.demo = animal
}
}
override fun toString(): String {
return "KotlinDemo(demo=$demo)"
}
}
fun main() {
var demo = KotlinDemo<Cat>(Cat())
println(demo)
}
~~~
结果:
```
KotlinDemo(demo=Cat(name='Cat', weight=30))
```
# 4. 协变与逆变
协变与逆变其实是`C#`语言`4.0`以后新增的高级特性,**协变**是将父类变为具体子类,协变类型作为消费者,只能读取不能写入,**逆变**是将子类变为具体父类,逆变作为生产者,只能写入不能读取。
- **协变**,父类变子类,只能读取(`out`);
- **逆变**,子类变父类,只能写入(`in`);
对于`in`和`out`这两个单词的简单理解记忆图示:
![](https://img.kancloud.cn/de/d4/ded4f553c090c91072729af78abe8f1b_349x301.png)
上图来自视频:[Kotlin教程](https://www.bilibili.com/video/BV1wf4y1s7TG?p=114)
这么说起来比较抽象,这里直接给出两个案例。
## 4.1 案例一:逆变
~~~
// 逆变,只写,作为函数参数
interface Consumer<in T>{
fun consume(item: T)
}
open class Animal
class Cat: Animal()
class KotlinDemo2: Consumer<Animal> {
override fun consume(item: Animal){
}
}
fun main() {
var consumer1: Consumer<Cat> = KotlinDemo2()
}
~~~
## 4.2 案例二:协变
~~~
// 协变,只读,即仅作为返回值
interface Producer<out T>{
fun produce(): T
}
open class Animal
class Cat: Animal()
class KotlinDemo2: Producer<Animal> {
override fun produce(): Animal{
return Animal()
}
}
class KotlinDemo1: Producer<Cat> {
override fun produce(): Cat {
return Cat()
}
}
fun main() {
var consumer1: Producer<Animal> = KotlinDemo1()
}
~~~
父变子指的是:要求是`Animal`,但是赋值的为子类对象。
# 5. 泛型类型检查
有时候我们需要对传入的泛型的类型做一个检查,而在`Java`中我们通常使用反射来获取类型进而进行判断,比如:
~~~
interface B<T>{
T getInstance(T t);
}
public class A<T> implements B<T>{
@Override
public T getInstance(T t) {
if(t.getClass().getName() .equals("com.kotlinLearn.Animal")){
return t;
}
return (T) new Cat();
}
public static void main(String[] args) {
A<Animal> a = new A<>();
Animal instance = a.getInstance(new Cat());
System.out.println(instance.getClass().getName());
}
}
~~~
结果:
```
com.kotlinLearn.Cat
```
而在`Kotlin`中就不需要这么麻烦,可以直接使用关键字`inline`和`reified`进行判断:
~~~
open class Animal
class Cat: Animal()
class B{
inline fun<reified T> getInstance(item: () -> T): T {
val animal = Animal()
return if(animal is T){
animal
} else{
item()
}
}
}
~~~
- Kotlin语言基础
- Kotlin的简介
- Kotlin的变量和常见数据类型
- Kotlin的区间
- Kotlin的位运算
- Kotlin的容器
- Kotlin类型检查
- Kotlin的空值处理
- Kotlin的函数
- Kotlin的类
- Kotlin的委托
- Kotlin的延迟加载
- Kotlin的异常
- Kotlin的Lambda表达式
- Kotlin的高阶函数
- Kotlin的标准库中的高阶函数
- Kotlin的泛型
- Kotlin的表达式
- Kotlin的解构
- Kotlin的运算符重载
- Kotlin语言中级
- Kotlin的扩展函数
- Kotlin的扩展属性
- Kotlin的infix关键字
- Kotlin的DSL
- Kotlin的一些注解(和Java互调用)
- Kotlin的lateinit和by lazy
- Kotlin的反射
- Kotlin的匿名接口
- 安卓中的Kotlin
- 数据库操作Room