Kotlin中協(xié)變、逆變和不變示例詳解
前言
Kotlin 泛型的基本語法類似于 Java ,不過出于型變安全,不支持 Java 中的<? extends T>,<?super T> 通配符型變約束,而是采用類似 C# 的 in,out 用于支持協(xié)變和逆變,這同時避免了處理子類型化,父類化的問題(即Java泛型中典型的List<T> 不是 List<Object>的子類型的問題);
基本的泛型語法可以參考官方中文文檔:https://www.kotlincn.net/docs/reference/
泛型實參的繼承關系對泛型類型的影響
協(xié)變:泛型類型與實參的繼承關系相同
逆變:泛型類型與實參的繼承關系相反
不變:泛型類型沒有關系
協(xié)變點:返回值類型是泛型參數(shù)類型
逆變點:入?yún)㈩愋褪欠盒蛥?shù)類型
@UnsafeVariance:協(xié)變點違例,告訴編譯器,沒事,你就按照我的意思執(zhí)行
1、泛型
什么是泛型?泛化的類型或者是類型的抽象,鴨子類型(看起來像鴨子,走起來也像鴨子,就是鴨子類型)在靜態(tài)語言中的一種靜態(tài)實現(xiàn)
1、抽象類,是這個類的本質,它是什么
2、接口,關心類能夠做什么,行為能力
舉兩個例子
兩個數(shù)的比較大小
// 需要有對比的功能,沒有的話就會報錯a<b
//加入限制 Comparable 具有對比的功能
fun<T:Comparable<T>> maxOf(a:T,b:T):T{
return if (a<b) b else a
}
方法調用
val a=2
val b=3
val maxOf = maxOf(2, 3)
println("shiming "+maxOf)
輸出結果
shiming 3
讓一個類具備對比的能
data class Complex(val a:Double,val b:Double):Comparable<Complex>{
override fun compareTo(other: Complex): Int {
return (value()-other.value()).toInt()
}
fun value():Double{
return a*a+b*b
}
override fun toString(): String {
return "$a*$a+$b*$b="+(a*a+b*b)
}
}
方法調用
val Complex1=Complex(4.0,5.0)
val Complex2=Complex(5.0,6.0)
println("shiming Complex1="+Complex1)
println("shiming Complex2="+Complex2)
println("shiming"+Complex1.compareTo(Complex2))
輸出結果
04-16 11:22:10.824 26429-26429/com.kotlin.demo I/System.out: shiming Complex1=4.0*a+5.0*b=41.0 04-16 11:22:10.824 26429-26429/com.kotlin.demo I/System.out: shiming Complex2=5.0*a+6.0*b=61.0 04-16 11:22:10.824 26429-26429/com.kotlin.demo I/System.out: shiming-20
通過Demo的測試的結果的發(fā)現(xiàn):泛型不管你到底是什么,它只管你能夠做什么事情
定義多個泛型參數(shù)
kotlin中的例子
(1..2).map { println("shiming $it=="+it) }
/**
* Returns a list containing the results of applying the given [transform] function
* to each element in the original collection.
(1..2).map調用的底層的方法
*/
public inline fun <T, R> Iterable<T>.map(transform: (T) -> R): List<R> {
return mapTo(ArrayList<R>(collectionSizeOrDefault(10)), transform)
}
/** A function that takes 22 arguments. function中最多的參數(shù)22.一共有23個方法 */
public interface Function22<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, out R> : Function<R> {
/** Invokes the function with the specified arguments. */
public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
}
kotlin中的類傳入泛型
data class ComplexNumber< T : Number>(val a:T,val b:T){
override fun toString(): String {
return "$a*$a+$b*$b"
}
}
泛型的實現(xiàn)的機制
何為偽泛型(Java 、Kotlin)?編譯完了,泛型就沒有了(真正的原因就是最開始寫Java編譯器的幾個人偷懶取巧,留下了歷史問題,Martin Odersky爆料。Martin Odersky是Typesafe的聯(lián)合創(chuàng)始人,也是Scala編程語言的發(fā)明者。)
//按照重載的定義這兩個方法應該編譯的過的,但是Java和kotlin編譯完了成了object或者是沒有
fun needList(list:List<Double>){
}
fun needList(list:List<Int>){
}
通過反編譯可以看到,臥槽我的泛型沒有了
public static final void needList(@NotNull List list) {
Intrinsics.checkParameterIsNotNull(list, "list");
}
public static final void needList(@NotNull List list) {
Intrinsics.checkParameterIsNotNull(list, "list");
}

沒有編譯通過
何為真泛型(C#)?編譯完了,還在
如果把以上的代碼放在C#中,就不會報錯,原因是C#的泛型不僅存在于編譯器,也存在運行期
java1.5才有的泛型特性,迫于現(xiàn)實的需求!用的人太多,但是C#第一個版本也沒有泛型,但是用的人少,所以C#使用的是真泛型
但是有一種方式可以在編譯器得到泛型類型:
reified讓泛型參數(shù)具體化,定義在inline中 ,kotlin實現(xiàn)為偽泛型,需要這個關鍵字植入到調用出才可以
//inline inline可用內聯(lián)函數(shù)(inline function)消除這些額外內存開銷,
//說白了就是在調用處插入函數(shù)體代碼,以此減少新建函數(shù)棧和對象的內存開銷!
inline fun<reified T> getT(){
println("shiming"+T::class.java)
}
調用
getT<String>() getT<Double>() //通過反編譯得到的結果,說白了,其實就是打印了,這樣永遠都不會丟失 String var21 = "shiming" + String.class; System.out.println(var21); var21 = "shiming" + Double.class; System.out.println(var21);
得到
04-16 15:28:59.775 31782-31782/com.kotlin.demo I/System.out: shimingclass java.lang.String 04-16 15:28:59.775 31782-31782/com.kotlin.demo I/System.out: shimingclass java.lang.Double
在實際工作中可以這樣用
data class Person(val name:String,val age:Int){
//重寫,得到json字符串
override fun toString(): String {
return "{name="+"\""+name+"\","+"age="+age+"}"
}
}
//例子 通過inline把這個前面的代碼植入到后面
// reified讓泛型參數(shù)具體化,定義在inline中 ,kotlin實現(xiàn)為偽泛型,需要這個關鍵字植入到調用出才可以
inline fun <reified T> Gson.fromJson(json:String):T=fromJson(json,T::class.java)
//模擬網(wǎng)絡請求返回的json數(shù)據(jù),得到bean類
val person=Person("shiming",20)
println("shiming "+person)
val toString = person.toString()
val person1= Gson().fromJson<Person>(toString)
println("shiming person1"+person1)
//上面一段代碼的反編譯的結果,和java是一樣的,執(zhí)行的流程
Person person = new Person("shiming", 20);
String toString = "shiming " + person;
System.out.println(toString);
toString = person.toString();
Gson $receiver$iv = new Gson();
Person person1 = (Person)$receiver$iv.fromJson(toString, Person.class);
String var25 = "shiming person1" + person1;
System.out.println(var25);
具體的關系
f(⋅)是逆變(contravariant)的,當A≤B時有f(B)≤f(A)成立;
f(⋅)是協(xié)變(covariant)的,當A≤B時有成立f(A)≤f(B)成立;
f(⋅)是不變(invariant)的,當A≤B時上述兩個式子均不成立,即f(A)與f(B)相互之間沒有繼承關系。
協(xié)變
在kotlin中List不是Java中的List,它只是只讀的,查看源碼如下List<out E> ,看見Out就是協(xié)變的,只讀類型,List中根本沒有add的方法,不可添加元素
//out 協(xié)變 Number 是Int的父類,協(xié)變點函數(shù)得返回類型
val numberList:List<Number> = listOf<Int>(1,58)
public interface List<out E> : Collection<E> {
// Query Operations
override val size: Int
override fun isEmpty(): Boolean
//告訴編譯器 我知道,你不要管我知道怎么搞
override fun contains(element: @UnsafeVariance E): Boolean
override fun iterator(): Iterator<E>
// Bulk Operations
override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
// Positional Access Operations
/**
* Returns the element at the specified index in the list.、
返回值的類型是E
*/
public operator fun get(index: Int): E
// Search Operations
/**
* Returns the index of the first occurrence of the specified element in the list, or -1 if the specified
* element is not contained in the list.
*/
public fun indexOf(element: @UnsafeVariance E): Int
/**
* Returns the index of the last occurrence of the specified element in the list, or -1 if the specified
* element is not contained in the list.
*/
public fun lastIndexOf(element: @UnsafeVariance E): Int
// List Iterators
/**
* Returns a list iterator over the elements in this list (in proper sequence).
*/
public fun listIterator(): ListIterator<E>
/**
* Returns a list iterator over the elements in this list (in proper sequence), starting at the specified [index].
*/
public fun listIterator(index: Int): ListIterator<E>
// View
/**
* Returns a view of the portion of this list between the specified [fromIndex] (inclusive) and [toIndex] (exclusive).
* The returned list is backed by this list, so non-structural changes in the returned list are reflected in this list, and vice-versa.
*
* Structural changes in the base list make the behavior of the view undefined.
*/
public fun subList(fromIndex: Int, toIndex: Int): List<E>
}
逆變:Comparable接口
//in 逆變 ,泛型的繼承關系相反 逆變點就是函數(shù)參數(shù)的類型 Any是Int的父類
val intComparable:Comparable<Int> = object :Comparable<Any>{
override fun compareTo(other: Any): Int {
return 0
}
}
public interface Comparable<in T> {
public operator fun compareTo(other: T): Int
}
不變:MutableList相當于Java中的|ArrayList,可讀可寫,不可變,泛型沒有in 或者是out ,泛型的繼承關系也沒有具體的關系,前面是后面的子類或者是后面是前面的子類,都是不成立。

public interface MutableList<E> : List<E>, MutableCollection<E> {
override fun add(element: E): Boolean
override fun remove(element: E): Boolean
override fun addAll(elements: Collection<E>): Boolean
public fun addAll(index: Int, elements: Collection<E>): Boolean
override fun removeAll(elements: Collection<E>): Boolean
override fun retainAll(elements: Collection<E>): Boolean
override fun clear(): Unit
public operator fun set(index: Int, element: E): E
public fun add(index: Int, element: E): Unit
public fun removeAt(index: Int): E
override fun listIterator(): MutableListIterator<E>
override fun listIterator(index: Int): MutableListIterator<E>
override fun subList(fromIndex: Int, toIndex: Int): MutableList<E>
}
星投影:始終找最安全的解決方法,安全方式是定義泛型類型的這種投影,該泛型類型的每個具體實例化將是該投影的子類型
如果泛型類型具有多個類型參數(shù),則每個類型參數(shù)都可以單獨投影。
例如,如果類型被聲明為 interface Function <in T, out U> ,可以想象以下星投影:
Function<*, String> 表示 Function<in Nothing, String> ;
Function<Int, > 表示 Function<Int, out Any?> ;
Function<, *> 表示 Function<in Nothing, out Any?> 。
可用的星投影的地方
//out 協(xié)變 Number 是Int的子類,協(xié)變點函數(shù)得返回類型
val numberList:List<*> = listOf<Int>(1,58)
val any = numberList[1] //星投影,去找父類
//in 逆變 ,泛型的繼承關系相反 逆變點就是函數(shù)參數(shù)的類型
val intComparable:Comparable<*> = object :Comparable<Any>{
override fun compareTo(other: Any): Int {
return 0
}
}
//星投影,去找父類 Nothing
intComparable.compareTo()
fun <T> hello(){
}
open class Hello<T>{
}
//這樣 就可以使用星投影
class Hello33<T>
//這樣也可以使用星投影
class Hello2:Hello<Hello<*>>()
class Hello332:Hello<Hello33<*>>()
在kotlin中調用java的類
//這樣也可以使用星投影
val raw:Raw<*> = Raw.getRaw()
public class Raw<T> {
@Override
public String toString() {
return "老子是Raw";
}
public static Raw getRaw(){
return new Raw();
}
}
不可以使用星投影的地方
//不變的話,就根本沒有繼承關系,沒有任何的關系 原因是這樣不安全
// val list1:MutableList<Number> = mutableListOf<Int>(1,5,4)
list1.add(BigDecimal(1244444444))
// val list2:MutableList<Int> = mutableListOf<Number>(1,5,4)
//泛型的實參不要使用星號
// val numberList11d:List<*> = listOf<*>(1,58)
//
// hello<*>()
//
// val hello: Any = Hello<*>()
fun <T> hello(){
}
open class Hello<T>{
}
安卓中一個MvpDemo,使用到了星投影和協(xié)變!
package com.kotlin.demo.star_demo
import org.jetbrains.annotations.NotNull
import java.lang.reflect.ParameterizedType
/**
* author: Created by shiming on 2018/4/14 15:08
* mailbox:lamshiming@sina.com
*/
//Mvp 中的V層 超級接口
interface IView<out P:Ipresenter<IView<P>>>{
val presenter:P
}
//P層的超級接口
interface Ipresenter<out V:IView<Ipresenter<V>>>{
// @NotNull
// IView getView();
val view:V
}
abstract class BaseView<out P:BasePresenter<BaseView<P>>>:IView<P>{
override val presenter:P
init {
presenter= findPresenterClass().newInstance()
presenter.view=this
}
/**
* 得到相對于的Class的文件
*/
private fun findPresenterClass():Class<P>{
//不知道,使用星投影去接收 相當于 Class thisClass = this.getClass();
var thisClass:Class<*> = this.javaClass
// while(true) {
// Type var10000 = thisClass.getGenericSuperclass();
// if(!(var10000 instanceof ParameterizedType)) {
// var10000 = null;
// }
// ParameterizedType var5 = (ParameterizedType)var10000;
// if(var5 != null) {
// Type[] var6 = var5.getActualTypeArguments();
// if(var6 != null) {
// var10000 = (Type)ArraysKt.firstOrNull((Object[])var6);
// if(var10000 != null) {
// Type var2 = var10000;
// if(var2 == null) {
// throw new TypeCastException("null cannot be cast to non-null type java.lang.Class<P>");
// }
//
// return (Class)var2;
// }
// }
// }
// }
//以下的代碼相當于上面的代碼
while (true){
(thisClass.genericSuperclass as? ParameterizedType)
?.actualTypeArguments
?.firstOrNull()
?.let {
return it as Class<P>
}?.run{
thisClass=thisClass.superclass ?:throw IllegalAccessException()
}
}
}
}
abstract class BasePresenter<out V:IView<BasePresenter<V>>>:Ipresenter<V>{
//lateinit 延遲初始化
//@UnsafeVariance 告訴編譯器 我很安全 不要管我
override lateinit var view:@UnsafeVariance V
}
class MainView:BaseView<MainPresenter>()
class MainPresenter:BasePresenter<MainView>()
class Mvp{
init {
MainView().presenter.let(::println)
//相當于下面的代碼
// BasePresenter var1 = (new MainView()).getPresenter();
// System.out.println(var1);
MainView().presenter.let { println("shiming P="+it) }
//相當于下面的代碼
// var1 = (new MainView()).getPresenter();
// MainPresenter it = (MainPresenter)var1;
// String var3 = "shiming P=" + it;
// System.out.println(var3);
// (new MainPresenter()).getView();
}
}
輸出的結果是:shiming P=com.kotlin.demo.star_demo.MainPresenter@fc35795
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關文章
Android實現(xiàn)QQ新用戶注冊界面遇到問題及解決方法
這篇文章主要介紹了Android實現(xiàn)QQ新用戶注冊界面遇到問題及解決方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-09-09
Android ViewDragHelper仿淘寶拖動加載效果
這篇文章主要為大家詳細介紹了Android ViewDragHelper仿淘寶拖動加載效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-08-08
Android系統(tǒng)view與SurfaceView的基本使用及區(qū)別分析
這篇文章主要為大家介紹了Android系統(tǒng)view與SurfaceView基本使用的案例分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步2022-03-03

