新JEP将简化Java类型变异
新的JEP Candidate旨在簡化處理Java中復雜的類型變異的概念。這個新的JEP Candidate可能會在Java 10中推出,提供了在定義的泛型類型中指定目標對象默認變異的方法,而不是在泛型類型實例化時通過通配符指定。這個新方案并不會代替通配符,而是減少對通配符的需求。
\\類型變異這個概念對于很多開發人員來說仍然比較模糊,在Java中通過不太普及的通配符來解決這個問題并沒有很大幫助。因此,為了幫助我們的讀者能夠理解這款JEP的潛在影響力,在本文中我們將首先解釋什么是類型變異,目前Java中是怎么解決它的,之后將介紹這個新方案能實現什么。
\\變異、協變和逆變
\\以下的代碼屬于傳統的在線購物應用程序:
\\public class Product {\/* ... */\}\\public class FrozenProduct extends Product {\ /* ... */\}\\\如果有個方法scan(Product product),我們調用它傳遞FrozenProduct對象,調用工作沒有問題,這是眾所周知的多態性的一般規則。但是當參數中包含泛型時,就不能使用相同的邏輯,以下的代碼將無法編譯:
\\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\}\\\當在Java中使用泛型時,并沒有關于目標類型和其子類型或超類型之間兼容性的假設。換句話說,當使用泛型時,我們默認目標類型是不變的,它只接受明確的類型。
\\然而,在上面的例子中,我們可以看到addAllProducts方法可以處理List of Product或是其子類型。當泛型參數可以接受其目標類型或是它的任何子類型,我們就說這個類型是協變的,在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\}\\\在這些例子中,接受的目標類型的變異是子類型。在一些其他例子中,目標類型的變異不是子類型,而是超類型。考慮以下的情況:
\\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中不會發生問題,然而,編譯器不允許我們這么做。askQuestion可以處理Predicate of String或其任意超類型:我們就說這種情況下的目標類型是逆變的,在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\}\\\通配符是創建類型變異的一種非常靈活的方法,因為它允許你在不同地方對同種類型定義不同的變異。比如說,在上面的例子里我們定義addAllProducts是協變的參數,但在其他地方根據我們的需求,可以定義它為逆變或是不變的。然而缺點是必須在每個地方根據需要明確指定變異,這樣會造成很多的冗余和混亂。所以新的方案應運而生。
\\在聲明時指定默認變異
\\通配符的最主要的問題是它們比開發人員通常需要的還要靈活。在Predicate\u0026lt;String\u0026gt;的例子中,我們理論上可以創建一個方法Predicate\u0026lt;? extends String\u0026gt;,然而,可以用到的用例有限(可能根本沒有)。在大量情況下,只有一個類型變異有意義,為了反映出這一點,JEP 300提供了在聲明泛型類型時指定默認變異的方法,而不是在實例化時指定默認變異。比如說,用了這種方案,可以使用逆變的關鍵字Predicate\u0026lt;contravariant T\u0026gt;來重寫接口Predicate\u0026lt;T\u0026gt;,這就代表著任何時候開發人員寫Predicate\u0026lt;String\u0026gt;都會被隱含地理解為Predicate\u0026lt;? super String\u0026gt;。
\\這個新功能的語法尚未決定,但是已經有了一些備選項:使用新的顯式關鍵字,如Function\u0026lt;contravariant T, covariant R\u0026gt;,或遵循其他語言的先例,如Scala中的符號(Function\u0026lt;-T, +R\u0026gt;),或是C#中的較短關鍵字(Function\u0026lt;in T, out R\u0026gt;)。在解決語法問題之前,還需要解決一些重要的技術問題,即默認變異和通配符之間的交互,默認變異對現有代碼產生的影響,以及變異類型兼容性檢查的實際機制。
\\最后值得提出的一點是,JEP 300僅會處理新的默認變異,但不會修改Java庫中可用的任何類和接口。如果之后JEP 300再發展可能會考慮處理這種情況,但也只是在其他版本的JEP中執行。
\\查看英文原文:New JEP Would Simplify Java Type Variance
總結
以上是生活随笔為你收集整理的新JEP将简化Java类型变异的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android 插件化总结
- 下一篇: 使用Flask-Mail发送邮件