溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Kotlin中協(xié)變、逆變和不變的示例分析

發(fā)布時間:2021-07-26 14:22:17 來源:億速云 閱讀:165 作者:小新 欄目:移動開發(fā)

小編給大家分享一下Kotlin中協(xié)變、逆變和不變的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

前言

Kotlin 泛型的基本語法類似于 Java ,不過出于型變安全,不支持 Java 中的<? extends T>,<?super T> 通配符型變約束,而是采用類似 C# 的 in,out 用于支持協(xié)變和逆變,這同時避免了處理子類型化,父類化的問題(即Java泛型中典型的List<T> 不是 List<Object>的子類型的問題);

基本的泛型語法可以參考官方中文文檔:https://www.kotlincn.net/docs/reference/

泛型實參的繼承關(guān)系對泛型類型的影響

協(xié)變:泛型類型與實參的繼承關(guān)系相同

逆變:泛型類型與實參的繼承關(guān)系相反

不變:泛型類型沒有關(guān)系

協(xié)變點:返回值類型是泛型參數(shù)類型

逆變點:入?yún)㈩愋褪欠盒蛥?shù)類型

@UnsafeVariance:協(xié)變點違例,告訴編譯器,沒事,你就按照我的意思執(zhí)行

1、泛型

什么是泛型?泛化的類型或者是類型的抽象,鴨子類型(看起來像鴨子,走起來也像鴨子,就是鴨子類型)在靜態(tài)語言中的一種靜態(tài)實現(xiàn)

1、抽象類,是這個類的本質(zhì),它是什么

2、接口,關(guān)心類能夠做什么,行為能力

舉兩個例子

兩個數(shù)的比較大小

// 需要有對比的功能,沒有的話就會報錯a<b
//加入限制 Comparable 具有對比的功能
fun<T:Comparable<T>> maxOf(a:T,b:T):T{
 return if (a<b) b else a
}

方法調(diào)用

val a=2
val b=3
val maxOf = maxOf(2, 3)
 println("shiming "+maxOf)

輸出結(jié)果

 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)
 }
}

方法調(diào)用

 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))

輸出結(jié)果

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的測試的結(jié)果的發(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調(diào)用的底層的方法
 */
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ā)明者。)

//按照重載的定義這兩個方法應(yīng)該編譯的過的,但是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");
 }

Kotlin中協(xié)變、逆變和不變的示例分析

沒有編譯通過

何為真泛型(C#)?編譯完了,還在

如果把以上的代碼放在C#中,就不會報錯,原因是C#的泛型不僅存在于編譯器,也存在運行期

java1.5才有的泛型特性,迫于現(xiàn)實的需求!用的人太多,但是C#第一個版本也沒有泛型,但是用的人少,所以C#使用的是真泛型

但是有一種方式可以在編譯器得到泛型類型:

reified讓泛型參數(shù)具體化,定義在inline中 ,kotlin實現(xiàn)為偽泛型,需要這個關(guān)鍵字植入到調(diào)用出才可以

//inline inline可用內(nèi)聯(lián)函數(shù)(inline function)消除這些額外內(nèi)存開銷,
//說白了就是在調(diào)用處插入函數(shù)體代碼,以此減少新建函數(shù)棧和對象的內(nèi)存開銷!
inline fun<reified T> getT(){
 println("shiming"+T::class.java)
}

調(diào)用

getT<String>()
getT<Double>()
//通過反編譯得到的結(jié)果,說白了,其實就是打印了,這樣永遠都不會丟失
  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){
 //重寫,得到j(luò)son字符串
 override fun toString(): String {
  return "{name="+"\""+name+"\","+"age="+age+"}"
 }
}

//例子 通過inline把這個前面的代碼植入到后面
// reified讓泛型參數(shù)具體化,定義在inline中 ,kotlin實現(xiàn)為偽泛型,需要這個關(guān)鍵字植入到調(diào)用出才可以
inline fun <reified T> Gson.fromJson(json:String):T=fromJson(json,T::class.java)
//模擬網(wǎng)絡(luò)請求返回的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)

//上面一段代碼的反編譯的結(jié)果,和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);

具體的關(guān)系

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)相互之間沒有繼承關(guān)系。

協(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 逆變 ,泛型的繼承關(guān)系相反 逆變點就是函數(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 ,泛型的繼承關(guān)系也沒有具體的關(guān)系,前面是后面的子類或者是后面是前面的子類,都是不成立。

Kotlin中協(xié)變、逆變和不變的示例分析

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 逆變 ,泛型的繼承關(guān)系相反 逆變點就是函數(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中調(diào)用java的類

//這樣也可以使用星投影
 val raw:Raw<*> = Raw.getRaw()
public class Raw<T> {
  @Override
  public String toString() {
    return "老子是Raw";
  }
  public static Raw getRaw(){
    return new Raw();
  }
}

不可以使用星投影的地方

//不變的話,就根本沒有繼承關(guān)系,沒有任何的關(guān)系 原因是這樣不安全
//    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();

  }
}

輸出的結(jié)果是:shiming P=com.kotlin.demo.star_demo.MainPresenter@fc35795

以上是“Kotlin中協(xié)變、逆變和不變的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學習更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI