Java 8星期五:Java 8的阴暗面
在Data Geekery ,我們喜歡Java。 而且,由于我們真的很喜歡jOOQ的流暢的API和查詢DSL ,我們對Java 8將為我們的生態系統帶來什么感到非常興奮。
Java 8星期五
每個星期五,我們都會向您展示一些不錯的教程風格的Java 8新功能,這些功能利用了lambda表達式,擴展方法和其他好東西。 您可以在GitHub上找到源代碼 。
Java 8的陰暗面
到目前為止,我們已經展示了該新主要版本中令人激動的部分 。 但是也有一些警告。 其中很多。 那件事
- ……令人困惑
- … 錯了
- …被省略(目前)
- …被省略(很長時間)
Java主要發行版總是有兩個方面。 從好的方面來說,我們獲得了許多新功能,大多數人會認為這是過期的 。 其他語言,平臺早在Java 5之前就有泛型。其他語言,平臺早在Java 8之前就有lambda。但是現在,我們終于有了這些功能。 在通常的古怪Java方式中。
Lambda表達式的引入非常優美。 從向后兼容的角度來看,能夠將每個匿名SAM實例編寫為lambda表達式的想法非常引人注目。 那么,什么是黑暗面到Java 8?
超載變得更糟
重載,泛型和vararg不是朋友。 我們已經在上一篇文章以及這個Stack Overflow問題中 對此進行了解釋 。 在您的奇怪應用程序中,這些可能并非每天都有問題,但對于API設計人員和維護人員而言,這是非常重要的問題。
使用lambda表達式,事情變得“更糟”。 因此,您認為可以提供一些便利的API,重載現有的run()方法(該方法接受Callable也接受新的Supplier類型:
static <T> T run(Callable<T> c) throws Exception {return c.call(); }static <T> T run(Supplier<T> s) throws Exception {return s.get(); }看起來非常有用的Java 7代碼現在是Java 8的一大難題。 因為您不能僅使用lambda參數簡單地調用這些方法:
public static void main(String[] args) throws Exception {run(() -> null);// ^^^^^^^^^^ ambiguous method call }倒霉。 您將不得不采用以下任一“經典”解決方案:
run((Callable<Object>) (() -> null));run(new Callable<Object>() {@Overridepublic Object call() throws Exception {return null;}});因此,盡管總有變通辦法,但這些變通辦法總是“糟透了”。 即使從向后兼容的角度來看事情不會中斷,這也真是令人沮喪。
并非所有關鍵字都支持默認方法
默認方法是一個不錯的補充。 有人可能聲稱Java最終具有特質 。 其他人顯然與該術語脫離了關系,例如Br??ian Goetz:
向Java添加默認方法的主要目標是“接口演變”,而不是“窮人的特征”。
如lambda-dev郵件列表中所示。
事實是,默認方法與Java中的其他任何東西相比都有很多正交和不規則的特征。 這里有一些批評:
他們不能成為最終的
鑒于默認方法也可以用作API中的便捷方法:
public interface NoTrait {// Run the Runnable exactly oncedefault final void run(Runnable r) {// ^^^^^ modifier final not allowedrun(r, 1);}// Run the Runnable "times" timesdefault void run(Runnable r, int times) {for (int i = 0; i < times; i++)r.run();} }不幸的是,以上操作是不可能的,因此第一個重載的便捷方法可能會在子類型中被覆蓋,即使這對API設計人員而言毫無意義。
無法使其同步
mm! 用語言難以實現嗎?
public interface NoTrait {default synchronized void noSynchronized() {// ^^^^^^^^^^^^ modifier synchronized// not allowedSystem.out.println("noSynchronized");} }是的, synchronized很少使用,就像決賽。 但是,當您有了用例時,為什么不僅僅允許它呢? 是什么使接口方法主體如此特別?
默認關鍵字
這也許是所有功能中最怪異和最不規則的。 default關鍵字本身。 讓我們比較一下接口和抽象類:
// Interfaces are always abstract public /* abstract */ interface NoTrait {// Abstract methods have no bodies// The abstract keyword is optional/* abstract */ void run1();// Concrete methods have bodies// The default keyword is mandatorydefault void run2() {} }// Classes can optionally be abstract public abstract class NoInterface {// Abstract methods have no bodies// The abstract keyword is mandatoryabstract void run1();// Concrete methods have bodies// The default keyword mustn't be usedvoid run2() {} }如果從頭開始重新設計該語言,則可能不需要任何abstract或default關鍵字。 兩者都是不必要的。 存在或不存在主體的事實足以使編譯器評估方法是否抽象。 即,情況應該如何:
public interface NoTrait {void run1();void run2() {} }public abstract class NoInterface {void run1();void run2() {} }上面會更精簡和更常規。 遺憾的是,EG從未真正討論過default的用途。 好吧,這是經過辯論的,但是EG從來不想接受這種選擇。 我已經嘗試過運氣,下面的響應 :
我不認為#3是一種選擇,因為與方法主體的接口一開始是不自然的。 至少指定“默認”關鍵字為讀者提供了某種語言來說明語言允許方法主體的原因。 就個人而言,我希望接口將保持純合同形式(不執行),但是我不知道有更好的選擇來發展接口。
同樣,這是EG的明確承諾,即不承諾Java中的“特征”。 默認方法是實現1-2個其他功能的純粹必要手段。 他們從一開始就沒有精心設計。
其他修飾符
幸運的是,在項目后期,使用了static修飾符使其成為了規范。 因此現在可以在接口中指定靜態方法。 但是由于某種原因,這些方法不需要(也不允許!) default關鍵字,它必須是EG完全隨機決定的,就像您顯然無法在接口中定義static final方法一樣。
雖然可見性修飾符已在lambda-dev郵件列表中進行了討論 ,但超出了此版本的范圍。 也許,我們可以在將來的版本中獲得它們。
實際上很少執行默認方法
有些方法在接口上會有合理的默認實現–可能會猜測。 直觀地,像List或Set這樣的collections接口會將它們放在其equals()和hashCode()方法上,因為這些方法的協定在接口上定義良好。 它還使用listIterator()在AbstractList實現,對于大多數定制列表而言,這是合理的默認實現。
如果對這些API進行改造,以使使用Java 8更加容易實現自定義集合,那就太好了。例如,我可以使我的所有業務對象都實現List ,而不會浪費AbstractList上的單個基類繼承。
但是,可能有一個與向后兼容性相關的令人信服的原因阻止了Oracle的Java 8團隊實現這些默認方法。 凡是向我們發送此原因的人都將獲得免費的jOOQ標簽 !
不是在這里發明的-心態
在lambda-dev EG郵件列表中也多次批評了這一點。 而且在撰寫本博客系列文章時 ,我只能確認新的功能接口讓人很難記住。 由于這些原因,他們感到困惑:
一些原始類型比其他更平等
與所有其他類型相比, int , long , double基本類型是首選的,因為它們在java.util.function包以及整個Streams API中都具有功能接口。 boolean是二等公民,因為它仍然把它做成包在一個形式BooleanSupplier或Predicate ,或者更糟: IntPredicate 。
所有其他原始類型在該區域中實際上并不存在。 即沒有針對byte , short , float和char特殊類型。 盡管滿足最后期限的爭論無疑是有效的,但這種古怪的現狀將使新手很難學習這種語言。
類型不僅僅稱為函數
坦白說吧。 所有這些類型都只是“功能”。 沒有人真正關心Consumer , Predicate , UnaryOperator等之間的隱式差異。
實際上,當您尋找具有非void返回值和兩個參數的類型時,您可能會調用什么呢? Function2 ? 好吧,你錯了。 它稱為BiFunction 。
這是一個決策樹,用于了解您要查找的類型如何被調用:
- 您的函數返回void嗎? 叫做Consumer
- 您的函數返回boolean嗎? 這叫做Predicate
- 您的函數返回int , long , double嗎? 它叫做XXToIntYY , XXToLongYY , XXToDoubleYY東西
- 您的函數沒有參數嗎? 叫做Supplier
- 您的函數是否接受一個int , long , double參數? 它稱為IntXX , LongXX , DoubleXX東西
- 您的函數有兩個參數嗎? 叫做BiXX
- 您的函數是否接受兩個相同類型的參數? 叫做BinaryOperator
- 您的函數返回的類型是否與作為單個參數的類型相同? 叫做UnaryOperator
- 您的函數是否接受兩個參數,其中第一個是引用類型,第二個是原始類型? 它稱為ObjXXConsumer (只有使用該配置的使用者存在)
- 否則:稱為Function
好主啊! 最近,我們當然應該去Oracle Education檢查一下Oracle Certified Java Programmer課程的價格是否急劇上漲了……幸運的是,有了Lambda表達式,我們幾乎不必記住所有這些類型!
有關Java 8的更多信息
Java 5泛型為Java語言帶來了許多很棒的新功能。 但是也有很多與類型擦除有關的警告。 Java 8的默認方法,Streams API和lambda表達式將再次為Java語言和平臺帶來很多很棒的新功能。 但是我們確信, Stack Overflow很快就會因在Java 8叢林中迷路的困惑程序員而引發問題。
學習所有新功能并非易事,但是新功能(和警告)仍然存在。 如果您是Java開發人員,則最好在有機會的時候立即開始練習。 因為我們還有很長的路要走。
翻譯自: https://www.javacodegeeks.com/2014/04/java-8-friday-the-dark-side-of-java-8.html
總結
以上是生活随笔為你收集整理的Java 8星期五:Java 8的阴暗面的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux命令常用命令(linux 命令
- 下一篇: 工程竣工验收备案制度需提交的材料包括哪些