<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # 泛型 與 Java 中一樣,Kotlin 中的類可以有類型參數: ``` kotlin class Box<T>(t: T) { var value = t } ``` 通常,要創建一個類的實例,我們需要提供類型實參: ``` kotlin val box: Box<Int> = Box<Int>(1) ``` 但是如果參數可以推斷,例如從構造器實參或通過其它的手段,都允許省略類型實參: ``` kotlin val box = Box(1) // 1 有類型 Int,因此編譯器會以整數看待,我們稱之為 Box<Int> ``` ## 變化 Java 類型系統最復雜部分的其中之一的通配符類型(查看[Java 泛型問答](http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html)) Kotlin 并沒有。作為替代,它有兩種其它的東西:聲明場所差異和類型預測。 首先,讓我們想一想關于為什么 Java 需要那些詭異的通配符。問題就是在 [Effective Java](http://www.oracle.com/technetwork/java/effectivejava-136174.html),Item 28:*Use bounded wildcards to increase API flexibility*。首先,Java 中的泛型是不變的,意思就是 `List<String>` 不是 `List<Object>` 的子類型。為什么是這樣?如果 List 不是不變的,它應該幾乎等于 Java 的數組,因此下面的代碼會通過編譯然后在運行時引發一個錯誤: ``` java // Java List<String> strs = new ArrayList<String>(); List<Object> objs = strs; // !!! 這里即將引發一個問題。Java 禁止這樣 objs.add(1); // 這里我們放置一個 Integer 到一個 String list 中 String s = strs.get(0); // !!! ClassCastException: Cannot cast Integer to String ``` 因此,Java 為了保證運行時的安全禁止這樣的東西。但這有一些隱患。例如,考慮 `Collection` 接口的 `addAll()` 方法,這個方法的簽名是什么?憑直覺我們應該這樣放置它: ``` java // Java interface Collection<E> ... { void addAll(Collection<E> items); } ``` 但另一方面,我們應該不能做到下面這樣簡單的事(完全是安全的): ``` java // Java void copyAll(Collection<Object> to, Collection<String> from) { to.addAll(from); // !!! 這種幼稚的 addAll 聲明不能被編譯 // Collection<String> 不是 Collection<Object> 的子類型 } ``` (Java 中,我們都學過這種困難的課程,查看 [Effective Java](http://www.oracle.com/technetwork/java/effectivejava-136174.html), Item 25: *Prefer lists to arrays*) 那就是為什么實際的 `addAll()` 簽名是下面這樣: ``` java // Java interface Collection<E> ... { void addAll(Collection<? extends E> items); } ``` **通配符類型實參** `? extends T` 指示這個方法接受*類型為 `T` 的一些子類型,而非 `T` 本身*的對象的集合。這個意思是我們可以從項目中安全地**讀** `T`(這個集合的元素都是一個 T 子類的實例),但**不能寫**進它,由于我們不知道什么對象才是 `T` 的未知子類型。在這種限制的返回中,我們有了所期望的行為: `Collection<String>` *是* `Collection<? extends T>` 的子類型。在“clever words”中,通配符與擴展邊界(上層邊界)一起構成了類型**協變**。 理解的關鍵在于為什么這個工作策略如此簡單:如果你可以僅僅從一個集合接受項目,然后很好地使用一個集合的 `String` 并讀取 `Object`。反過來,如果你可以僅僅_放置_項目到集合里,它可以很好地從拿取一個集合的 `Object` 并放置 `String` 到它里面:Java 中我們有 `List<Object>` 的超類 `List<? super String>`。 后者稱為**抗變性**,而且你可以在 `List<? super String>` 上只像實參一樣調用方法來拿取 String (例如,你可以調用 `add(String)` 或 `set(int, String)`),在此期間如果你調用什么在 `List<T>` 中返回了 `T`,那么你得不到一個 `String`,而是一個 `Object`。 Joshua Bloch 稱那些對象你只能從**生產者** **讀取**,并且只能 **寫入** 到 **消費者**。他推薦:“*為了彈性最大化,要在輸入的參數上使用通配符來表示生產者和消費者*”,并提出下列助記詞: *PECS 代表 生產者 Extends,消費者 Super* *注意*:如果你使用一個生產者對象,就是說 `List<? extends Foo>`,你不允許在這個對象上調用 `add()` 或者 `set()`,但這個意思不是說這個對象是**不變的**:例如,沒有什么阻止你調用 `clear()` 從 list 移除所有的項目,由于 `clear()` 根本不需要任何參數。僅僅只通過通配符(或其它可變類型)確保**類型安全**。不變性完全是另一回事。 ### 聲明場所差異 假設我們有一個泛型接口 `Source<T>`,沒有任何方法而只有一個 `T` 作為參數,僅有的方法是返回 `T`: ``` java // Java interface Source<T> { T nextT(); } ``` 然后,它應該是完美地在一個類型為 `Source<Object>` 中安全地存儲一個 `Source<String>` 實例引用 —— 沒有消費者方法可以被調用。但 Java 并不知道這一點,而仍然會禁止它: ``` java // Java void demo(Source<String> strs) { Source<Object> objects = strs; // !!! 在 Java 中是不允許的 // ... } ``` 要修復這一點,我們不得不以無意義的方式聲明類型為 `Source<? extends Object>` 的對象,因為我們可以像前面那樣調用這種變量上所有相同的方法,因此沒有值通過更復雜的類型被加入。但編譯器并不知道。 在 Kotlin 中,有一種途徑向編譯器說明這類東西。稱之為**聲明場所差異**:我們可以注解源碼的**類型參數** `T` 來確保它是只從 `Source<T>` 的成員**返回**(生產),而從不消費。對這個我們提供了 `out` 標識符: ``` kotlin abstract class Source<out T> { abstract fun nextT(): T } fun demo(strs: Source<String>) { val objects: Source<Any> = strs // 由于 T 是一個 out 參數,這樣就 OK 了 // ... } ``` 通用的規則是:當一個 `C` 類的類型參數 `T` 被聲明為 `out`,它會僅在 `C` 類成員的 `out` 位置存在,但返回的 `C<Base>` 可以安全地成為 `C<Derived>` 的超類。 在 clever words 中他們會說類 `C` 在參數 `T` 中是協變的,或者 `T` 是一個協變的類型參數。你可以想到 `C` 如同是 `T` 的**生產者**,并非 `T` 的**消費者**。 `out` 標識符被稱作**差異注解**,并且由于它是在聲明類型參數的場所提供的,我們就會說**聲明場所差異**。這是相對于 Java 的在通配符在類型協變用法中的**使用場所差異**而言的。 除 `out` 之外,Kotlin 提供了一個配套的真協變注解:`in`。它使得一個類型參數**逆變**:它僅能作為消費者并從不生產。逆變的一個好例子是類 `Comparable`: ``` kotlin abstract class Comparable<in T> { abstract fun compareTo(other: T): Int } fun demo(x: Comparable<Number>) { x.compareTo(1.0) // 1.0 有 Number 的子類 Double // 因此,我們可以把 x 賦值給一個類型為 Comparable<Double> 的變量 val y: Comparable<Double> = x // OK! } ``` 我們相信單詞 `in` 和 `out` 是自解釋的(就像它們有時也能成功地在 C# 中使用一樣),因此上面所提到的情況中并非真正需要,而是為了更高級的目的: **[The Existential](http://en.wikipedia.org/wiki/Existentialism) 變成:消費者 `in`,生產者 `out`!**:-) ## 類型投影 ### 使用場所可變:類型投影 這非常適合于聲明一個類型參數 T 為 `out` 并且不會在子類使用場所中遇到麻煩。當類考慮**能**被真正地限制為只返回 `T` 時是這樣,但如果不能呢?這個數組是個好例子: ``` kotlin class Array<T>(val size: Int) { fun get(index: Int): T { /* ... */ } fun set(index: Int, value: T) { /* ... */ } } ``` 這個類在 `T` 中既不能合作也不可逆變,并強制成不可變的。考慮下面的函數: ``` kotlin fun copy(from: Array<Any>, to: Array<Any>) { assert(from.size == to.size) for (i in from.indices) to[i] = from[i] } ``` 這個函數假定從一個數組復制項目到另一個。讓我們嘗試在實踐中應用它: ``` kotlin val ints: Array<Int> = arrayOf(1, 2, 3) val any = Array<Any>(3) copy(ints, any) // 錯誤:要求 (Array<Any>, Array<Any>) ``` 這里我們遇到一樣的熟悉的問題:`Array<T>` 的 `T` 是不可變的,因此 `Array<Int>` 和 `Array<Any>` 都不是彼此的子類型。為什么呢?再說一遍,因為復制可能會做壞事,意即它會嘗試寫入,就是說一個字符串給 `from`,而如果我們在那里又傳遞一個 `Int` 數組,那么 `ClassCastException` 會在后面某時被拋出。 此時,我們要做的事僅僅是確保 `copy()` 不會做什么壞事。我們阻止它向 `from` 寫入,于是我們可以: ``` kotlin fun copy(from: Array<out Any>, to: Array<Any>) { // ... } ``` 所發生的這些事稱之為**類型投影**:我們說過 `from` 不是簡單的一個數組,但它是一個受限制(**投影**)的數組:我們只能調用那些返回類型參數 `T` 的方法,在這種情況下它的意思是我們只能調用 `get()`。這是我們近似于**使用場所差異**,并符合 Java 的 `Array<? extends Object>`,但更簡單一點的方式。 你還可以設計一個與 `in` 一起的類型: ``` kotlin fun fill(dest: Array<in String>, value: String) { // ... } ``` `Array<in String>` 相當于 Java 的 `Array<? super String>`,意即你可以傳遞一個 `CharSequence` 數組或者 `Object` 數組到 `fill()` 函數。 ### 星號投影 有時你會說你對類型沖突一無所知,但仍想以安全的方式使用它。這個安全的方式在這里就是定義這種泛型的投影,泛型的每個具體的實例應該都是投影的一個子類型。 Kotlin 為這個提供了這個稱之為**星號投影**的語法 - 對于 `Foo<out T>`,`T` 是一個帶有上界 `TUpper` 的協變的類型參數,`Foo<*>` 等價于 `Foo<out TUpper>`。意思是當是 `T` 的時候無法你可以安全地從 `Foo<*>` *讀取* `TUpper` 的值。 - 對于 `Foo<in T>`,`T` 是一個協變的類型參數,`Foo<*>` 等價于 `Foo<in Nothing>`。意思是當 `T` 未知時沒有任何你可以安全**寫入**到 `Foo<*>`的方式。 - 對于 `Foo<T>`,`T` 是一個帶有上界 `TUpper` 的不變的類型參數,`Foo<*>`對于值讀取等價于 `Foo<out TUpper>`,對于值寫入等價于 `Foo<in Nothing>`。 如果一個泛型有幾個類型參數則它們每一個都能獨立地投影。例如,如果這個類型是聲明為 `interface Function<in T, out U>`則我們可以想像下面的星號投影: - `Function<*, String>` 意思是 `Function<in Nothing, String>`; - `Function<Int, *>` 意思是 `Function<Int, out Any?>`; - `Function<*, *>` 意思是 `Function<in Nothing, out Any?>`。 注意:星號投影與 Java 的原始類型非常相似,但很安全。 # 泛型函數 并非只有類可以有類型參數。函數也可以。類型參數放置在函數名前面: ``` kotlin fun <T> singletonList(item: T): List<T> { // ... } fun <T> T.basicToString() : String { // extension function // ... } ``` 如查類型參數在調用場所被明確地傳遞,它們要在函數名**后面**指定: ``` kotlin val l = singletonList<Int>(1) ``` # 泛型約束 可以替代給出的類型參數的可能的類型可以通過**泛型約束**加以限制。 ## 上界 大多數限制通用約束的類型是**上界**,與 Java 的 *extends* 關鍵字相符: ``` kotlin fun <T : Comparable<T>> sort(list: List<T>) { // ... } ``` 在冒號后面指定的類型就是**上界**:只有 `Comparable<T>` 的子類型可以代替 `T`。例如 ``` kotlin sort(listOf(1, 2, 3)) // OK。Int 是 Comparable<Int> 的子類型 sort(listOf(HashMap<Int, String>())) // 錯誤:HashMap<Int, String> 不是 Comparable<HashMap<Int, String>> 的子類型 ``` 默認的上界(如果未指定)是 `Any?`。尖括號里面只能指定一個上界。如果相同的類型參數需要多于一個上界,我們需要一個獨立的 **where** 子句: ``` kotlin fun <T> cloneWhenGreater(list: List<T>, threshold: T): List<T> where T : Comparable, T : Cloneable { return list.filter { it > threshold }.map { it.clone() } } ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看