新JEP将简化Java类型变异
新的JEP Candidate旨在簡(jiǎn)化處理Java中復(fù)雜的類型變異的概念。這個(gè)新的JEP Candidate可能會(huì)在Java 10中推出,提供了在定義的泛型類型中指定目標(biāo)對(duì)象默認(rèn)變異的方法,而不是在泛型類型實(shí)例化時(shí)通過(guò)通配符指定。這個(gè)新方案并不會(huì)代替通配符,而是減少對(duì)通配符的需求。
\\類型變異這個(gè)概念對(duì)于很多開(kāi)發(fā)人員來(lái)說(shuō)仍然比較模糊,在Java中通過(guò)不太普及的通配符來(lái)解決這個(gè)問(wèn)題并沒(méi)有很大幫助。因此,為了幫助我們的讀者能夠理解這款JEP的潛在影響力,在本文中我們將首先解釋什么是類型變異,目前Java中是怎么解決它的,之后將介紹這個(gè)新方案能實(shí)現(xiàn)什么。
\\變異、協(xié)變和逆變
\\以下的代碼屬于傳統(tǒng)的在線購(gòu)物應(yīng)用程序:
\\public class Product {\/* ... */\}\\public class FrozenProduct extends Product {\ /* ... */\}\\\如果有個(gè)方法scan(Product product),我們調(diào)用它傳遞FrozenProduct對(duì)象,調(diào)用工作沒(méi)有問(wèn)題,這是眾所周知的多態(tài)性的一般規(guī)則。但是當(dāng)參數(shù)中包含泛型時(shí),就不能使用相同的邏輯,以下的代碼將無(wú)法編譯:
\\private void addAllProducts(List\u0026lt;Product\u0026gt; products) {\/* Check product stock */\shoppingCart.addAll(products);\}\\private void freezerSection() {\ List\u0026lt;FrozenProduct\u0026gt; frozenProducts = /* select frozen products */;\ addAllProducts(frozenProducts); // ERROR: List\u0026lt;Product\u0026gt; expected\ // List\u0026lt;FrozenProduct\u0026gt; found\}\\\當(dāng)在Java中使用泛型時(shí),并沒(méi)有關(guān)于目標(biāo)類型和其子類型或超類型之間兼容性的假設(shè)。換句話說(shuō),當(dāng)使用泛型時(shí),我們默認(rèn)目標(biāo)類型是不變的,它只接受明確的類型。
\\然而,在上面的例子中,我們可以看到addAllProducts方法可以處理List of Product或是其子類型。當(dāng)泛型參數(shù)可以接受其目標(biāo)類型或是它的任何子類型,我們就說(shuō)這個(gè)類型是協(xié)變的,在Java中可以用extends表示:
\\private void addAllProducts(List\u0026lt;? extends Product\u0026gt; products) {\/* Check product stock */\shoppingCart.addAll(products);\}\\private void freezerSection() {\ List\u0026lt;FrozenProduct\u0026gt; frozenProducts = /* select frozen products */;\ addAllProducts(frozenProducts); // works with no problem\}\\\在這些例子中,接受的目標(biāo)類型的變異是子類型。在一些其他例子中,目標(biāo)類型的變異不是子類型,而是超類型。考慮以下的情況:
\\private boolean askQuestion(Predicate\u0026lt;String\u0026gt; p) {\return p.test(\"hello\");\}\\private void applyPredicate() {\ Predicate\u0026lt;Object\u0026gt; evenLength = o -\u0026gt; o.toString().length() % 2 == 0;\ askQuestion(evenLength); // ERROR: Predicate\u0026lt;String\u0026gt; expected\ // Predicate\u0026lt;Object\u0026gt; found\}\\\在這種情況下,我們可以看到使用string “hello”到lambda o -\u0026gt; o.toString().length() % 2 == 0中不會(huì)發(fā)生問(wèn)題,然而,編譯器不允許我們這么做。askQuestion可以處理Predicate of String或其任意超類型:我們就說(shuō)這種情況下的目標(biāo)類型是逆變的,在Java中可以用super表示:
\\private boolean askQuestion(Predicate\u0026lt;? super String\u0026gt; p) {\return p.test(\"hello\");\}\\private void applyPredicate() {\ Predicate\u0026lt;Object\u0026gt; evenLength = o -\u0026gt; o.toString().length() % 2 == 0;\ askQuestion(evenLength); // works with no problem\}\\\通配符是創(chuàng)建類型變異的一種非常靈活的方法,因?yàn)樗试S你在不同地方對(duì)同種類型定義不同的變異。比如說(shuō),在上面的例子里我們定義addAllProducts是協(xié)變的參數(shù),但在其他地方根據(jù)我們的需求,可以定義它為逆變或是不變的。然而缺點(diǎn)是必須在每個(gè)地方根據(jù)需要明確指定變異,這樣會(huì)造成很多的冗余和混亂。所以新的方案應(yīng)運(yùn)而生。
\\在聲明時(shí)指定默認(rèn)變異
\\通配符的最主要的問(wèn)題是它們比開(kāi)發(fā)人員通常需要的還要靈活。在Predicate\u0026lt;String\u0026gt;的例子中,我們理論上可以創(chuàng)建一個(gè)方法Predicate\u0026lt;? extends String\u0026gt;,然而,可以用到的用例有限(可能根本沒(méi)有)。在大量情況下,只有一個(gè)類型變異有意義,為了反映出這一點(diǎn),JEP 300提供了在聲明泛型類型時(shí)指定默認(rèn)變異的方法,而不是在實(shí)例化時(shí)指定默認(rèn)變異。比如說(shuō),用了這種方案,可以使用逆變的關(guān)鍵字Predicate\u0026lt;contravariant T\u0026gt;來(lái)重寫(xiě)接口Predicate\u0026lt;T\u0026gt;,這就代表著任何時(shí)候開(kāi)發(fā)人員寫(xiě)Predicate\u0026lt;String\u0026gt;都會(huì)被隱含地理解為Predicate\u0026lt;? super String\u0026gt;。
\\這個(gè)新功能的語(yǔ)法尚未決定,但是已經(jīng)有了一些備選項(xiàng):使用新的顯式關(guān)鍵字,如Function\u0026lt;contravariant T, covariant R\u0026gt;,或遵循其他語(yǔ)言的先例,如Scala中的符號(hào)(Function\u0026lt;-T, +R\u0026gt;),或是C#中的較短關(guān)鍵字(Function\u0026lt;in T, out R\u0026gt;)。在解決語(yǔ)法問(wèn)題之前,還需要解決一些重要的技術(shù)問(wèn)題,即默認(rèn)變異和通配符之間的交互,默認(rèn)變異對(duì)現(xiàn)有代碼產(chǎn)生的影響,以及變異類型兼容性檢查的實(shí)際機(jī)制。
\\最后值得提出的一點(diǎn)是,JEP 300僅會(huì)處理新的默認(rèn)變異,但不會(huì)修改Java庫(kù)中可用的任何類和接口。如果之后JEP 300再發(fā)展可能會(huì)考慮處理這種情況,但也只是在其他版本的JEP中執(zhí)行。
\\查看英文原文:New JEP Would Simplify Java Type Variance
總結(jié)
以上是生活随笔為你收集整理的新JEP将简化Java类型变异的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Android 插件化总结
- 下一篇: 使用Flask-Mail发送邮件