今天在阅读第三方框架底层源码时,用kotlin写的。遇到
主要分几个部分来总结一下:
/**
* @description 泛型类的定义
* @author 陈元
* @date 2020 07/11
*/
class Generate<T>(var t:T)
/**
* @description 泛型类定义多个泛型
* @author 陈元
* @date 2020 07/11
*/
class GenerateMore<T,K>(val t:T,val k:K)
/**
* @description 泛型接口的定义
* @author 陈元
* @date 2020 07/11
*/
interface GenerateInter<T>{
//使用传过来的泛型
fun useT(t:T)
}
/**
* @description 泛型接口,定义多个泛型
* @author 陈元
* @date 2020 07/11
*/
interface GenerateInterMore<T,M>{
//使用传过来的泛型
fun useT(t:T)
//使用传过来的多个泛型
fun useTM(t:T,m:M)
}
/**
* 4.泛型方法
*/
fun <T> generate(t:T){
println()
}
/**
* 4.定义多个泛型
*/
fun <T,L> generateMore(t:T,l:L){
}
好了,以上就是泛型的定义。
通过名字就知道,限定类型,即然是限定类型,那么肯定是限定在某一个类型之内,不能超过。
为了演示这个限定类型,我们来定义几个类。几个类的关系如图
代码示例:
/**
* @description 水果
* @author 陈元
* @date 2020 07/11
*/
open class Fruit{
open fun name(){
println("水果父亲")
}
}
/**
* @description 苹果
* @author 陈元
* @date 2020 07/11
*/
open class Apple : Fruit() {
override fun name() {
println("苹果")
}
}
/**
* @description 香蕉
* @author 陈元
* @date 2020 07/11
*/
class Banana : Fruit() {
override fun name() {
println("香蕉")
}
}
/**
* @description 小苹果
* @author 陈元
* @date 2020 07/11
*/
class SmallApple : Apple() {
override fun name() {
println("小苹果")
}
}
代码非常简单,我们在使用泛型类型限定。首先说一下语法 定义泛型时,使用
/**
* 1.泛型的类型限定
*/
class Generate<T:Apple>(var t:T)
通过这里,大家可以看到,我们在定义泛型T时,限定了我们传入的T只能是Apple类型,包括Apple的所有的孩子。
我们来测试下吧!
//因为限定了类型,所以传String是不行的
val generate = Generate("apple")
// 传入apple肯定可以,这点我们都知道
val generate2 = Generate(Apple())
//虽然Apple是派生自Fruit,这里传入Fruit依然不行
val generate1 = Generate(Fruit())
//这边传入Apple的孩子,也是可以的
val generate3 = Generate(SmallApple())
有人读到这里,说Java里泛型不仅可以限定一个类型,还可以限定多个类型,我们这里当然也是可以的了,我们来看看
**
* @description 泛型限定
* @author 陈元
* @date 2020 07/11
*/
/**
* 1.泛型的类型限定,限定多个类型
*/
class Generate<T>(var t:T) where T:Apple,T:Serializable
使用呢,是不是都符合,我们的期望,这里我们限定的两个类型
**
**
我们测试一下吧
这里我们先试一下,没有实现Serial… 这个接口的,
// 传入apple肯定可以,这点我们都知道
val generate4 = Generate(Apple())
我们运行,发现编译器报错了
e: Test.kt: (21, 21): Type parameter bound for T in constructor Generate(t: T) where T : Serializable
is not satisfied: inferred type Apple is not a subtype of Serializable
看到这个错,都知道了吧。这边同时还需要实现Serializable 接口,就可以了。
泛型接口,和 泛型方法,在使用的时候,类型限定都是一样的,这里就不一一展开了,大家可以去阅读官方文档哦。
官方文档地址
这边直接上代码解释,我们在使用泛型或设计框架时,泛型在使用泛型类时去继承异常,编译器直接报错,是无法使用的。以下就是 典型的错误的例子。
class GenerateTException<T> : Exception()
class GenerateTException2<T> : Throwable()
这里再说明一下,我们在使用泛型时,比如前面提到的 Apple 派生自 Fruit。本来在使用的时候是没有任何问题的。如下
val fruit:Fruit = Apple()
但是在使用泛型时,要注意,以下是错误代码
val generate:Generate<Fruit> = Generate<Apple>(Apple())
虽然在apple 是派生自水果,但是水果 Generate<> 和 苹果 Generate<> 是没有任何关系的。
本文的重点来了,out 和 in 这两个 原来是 java 里的 《?extends 类名》 和 《?super类名》
这边呢,咱们直接上代码解释
out
1.首先,我们定义一个泛型,如下:
/**
* @description 泛型
* @author 陈元
* @date 2020 07/11
*/
class Generate<T>(var t:T)
2.我们再定义一个方法,来使用泛型的通配符类形
fun generateFunc(generate:Generate<out Apple>){
println(generate.t.name())
}
这个方法的意思是,我接收一个Generate类型的参数,里面的泛型 out Apple表示,我这里接收的泛型只能是Apple和Apple的孩子或孙子。。,如代码所示:
val generate = Generate(Fruit())
val generate1 = Generate(Apple())
val generate2 = Generate(SmallApple())
generateFunc(generate) //这里报错,说明不可以传入 Fruit
generateFunc(generate1)
generateFunc(generate2)
只读模式
为什么这里,是只读模式呢,因为,在方法里面我们调用 generate.t.name() 这个方法的时候,我们可以获取。 但是如果我们,去重新赋值是不行的,如下
fun generateFunc(generate:Generate<out Apple>){
generate.t = SmallApple() //这句话报错,只能读,不能重新赋值,
println(generate.t.name())
}
in
现在,我们就把方法里面的out 换成 in,再来看看我们的调用
val generate = Generate(Fruit())
val generate1 = Generate(Apple())
val generate2 = Generate(SmallApple())
generateFunc(generate)
generateFunc(generate1)
generateFunc(generate2) //这里报错,说明不可以传入 smallApple也就是孩子
里面的泛型 in Apple表示,我这里接收的泛型只能是Apple和Apple的父亲或爷爷。。。
只写模式
fun generateFunc(generate:Generate<in Apple>){
generate.t = SmallApple()
println(generate.t.name()) //这句话报错,只能写,不能读,
}
知道了,泛型的使用后,知道泛型好神奇呀,那我们再看一看,一起探究一下,泛型底层的原理到底是怎么实现的,虚拟机是如何实现泛型的。我们还是通过代码来看看,如下
/**
* @description 探究泛型的底层原理
* @author 陈元
* @date 2020 07/11
*/
fun main() {
val generate = Generate(Fruit())
val generate1 = Generate(Apple())
println(generate.javaClass)
println(generate1.javaClass)
println(generate.javaClass == generate1.javaClass)
}
这边我们简单起见,就定义了两个类,然后我们输出genrate和generate1的class类型对象,得出来结果如下:
G:\androidStudio\Android\jre\bin\java.exe -javaagent:G:\androidStudio\Android\lib\idea_rt.jar=58896:G:\androidStudio\Android\bin -Dfile.encoding=UTF-8 -classpath G:\androidStudio\AndroidStudioSDk\platforms\android-29\android.jar;G:\androidStudio\AndroidStudioSDk\platforms\android-29\data\res;F:\android-eclipse-workspace\MyApplication\app\build\intermediates\javac\debug\classes;F:\android-eclipse-workspace\MyApplication\app\build\tmp\kotlin-classes\debug;F:\android-eclipse-workspace\MyApplication\app\build\generated\res\resValues\debug;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-android-extensions-runtime\1.3.50\bec16087637a7cafe54894e73d38037977cb30d2\kotlin-android-extensions-runtime-1.3.50.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-jdk7\1.3.50\50ad05ea1c2595fb31b800e76db464d08d599af3\kotlin-stdlib-jdk7-1.3.50.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib\1.3.50\b529d1738c7e98bbfa36a4134039528f2ce78ebf\kotlin-stdlib-1.3.50.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\org.jetbrains.kotlin\kotlin-stdlib-common\1.3.50\3d9cd3e1bc7b92e95f43d45be3bfbcf38e36ab87\kotlin-stdlib-common-1.3.50.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\org.jetbrains\annotations\13.0\919f0dfe192fb4e063e7dacadee7f8bb9a2672a9\annotations-13.0.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\androidx.collection\collection\1.1.0\1f27220b47669781457de0d600849a5de0e89909\collection-1.1.0.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\androidx.lifecycle\lifecycle-common\2.1.0\c67e7807d9cd6c329b9d0218b2ec4e505dd340b7\lifecycle-common-2.1.0.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\androidx.arch.core\core-common\2.1.0\b3152fc64428c9354344bd89848ecddc09b6f07e\core-common-2.1.0.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\androidx.annotation\annotation\1.1.0\e3a6fb2f40e3a3842e6b7472628ba4ce416ea4c8\annotation-1.1.0.jar;C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\androidx.constraintlayout\constraintlayout-solver\1.1.3\54abe9ffb22cc9019b0b6fcc10f185cc4e67b34e\constraintlayout-solver-1.1.3.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\b8f10ae6f5b38c660c7334f34edc8667\core-ktx-1.2.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\31016c6c0856b517b84ecfbcfb815ff0\appcompat-1.1.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\31016c6c0856b517b84ecfbcfb815ff0\appcompat-1.1.0\res;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\6ad38dfac4e106a4ff4e5e048c9d7854\cursoradapter-1.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\9c96e3c7610a71f64d933abf5d989c41\fragment-1.1.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\3c6026bf367a81ae0b26f6a0755e7c5c\appcompat-resources-1.1.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\3c6026bf367a81ae0b26f6a0755e7c5c\appcompat-resources-1.1.0\res;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\9eeb03172080be4be809eb3b789602a2\drawerlayout-1.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\d81313297acd4b62a12e1c4be346ea86\viewpager-1.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\d4d6f1b618f6740b3852fbc0a4f6708f\loader-1.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\47bf1c1672b530aea741a2298486de75\activity-1.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\8042381f1240df5f98ac8c5a464644dd\vectordrawable-animated-1.1.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\2485cb75ca766726d2f85318526894e3\vectordrawable-1.1.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\b403c837ac7d55e559d7b654aa580176\customview-1.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\415a936aa630178fa8f04bbb1a3bb075\core-1.2.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\415a936aa630178fa8f04bbb1a3bb075\core-1.2.0\res;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\5aa6e1ee8a4aa87ed09e4136bb1359d6\versionedparcelable-1.1.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\8be5234bafda9ab14ed7f88c9dd40d77\lifecycle-viewmodel-2.1.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\d5a39d8f51fcfc1b19c3ec4132b72e21\lifecycle-runtime-2.1.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\621e01429d79a08e6530f2907ad873bf\savedstate-1.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\e05a230acf535e3dec39f0f39ed5f0ca\interpolator-1.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\004fb91108e1b1927119709e2ff43030\lifecycle-livedata-2.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\3a3ed459510bf208b94d56b861c80196\lifecycle-livedata-core-2.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\ab93a8dbadb16e1d4464b7fe897665b9\core-runtime-2.0.0\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\c4b4b829cc770abdd306eef91b3e77f9\constraintlayout-1.1.3\jars\classes.jar;C:\Users\Administrator\.gradle\caches\transforms-2\files-2.1\c4b4b829cc770abdd306eef91b3e77f9\constraintlayout-1.1.3\res com.myapplication.TestKt
class com.myapplication.Generate
class com.myapplication.Generate
true
Process finished with exit code 0
这就尴尬了,明明是两个对象,为什么拿到的class对象为同一个呢。我们带着这个疑问,继续深究,我们去看一下底层的字节码 如下
public final static main()V
L0
LINENUMBER 8 L0
NEW com/myapplication/Generate
DUP
NEW com/myapplication/Fruit
DUP
INVOKESPECIAL com/myapplication/Fruit.<init> ()V //这边首先初始化了Fruit
INVOKESPECIAL com/myapplication/Generate.<init> (Ljava/lang/Object;)V //这边初始化Generate时泛型的类型 竟然是object.
ASTORE 0
L1
LINENUMBER 10 L1
NEW com/myapplication/Generate
DUP
NEW com/myapplication/Apple
DUP
INVOKESPECIAL com/myapplication/Apple.<init> ()V
INVOKESPECIAL com/myapplication/Generate.<init> (Ljava/lang/Object;)V
ASTORE 1
我嚓,我嚓,什么鬼,我们看一下, 请看注释处,我们首先初始化了Fruit对象,然后我们初始化对象Generate 时,泛型变成Objcet了。再一看呢,最后第二行,初始化对象Generate时 泛型同样也变成Object了
泛型嚓除
原来虚拟机在实现泛型的时候,先把类型给嚓除了,变成了Object .