發(fā)布于:2021-02-12 00:00:07
0
301
0
Java 8很好地竊取了Scala的想法,但是這里列出了Java開發(fā)人員仍然夢寐以求的Ceylon編程功能。
當(dāng)Hibernate完成并且功能完善并且需要新的挑戰(zhàn)時(shí),該怎么辦?對(duì)。人們創(chuàng)建了一種新的JVM語言,稱為Ceylon。
2013年11月12日,Ceylon 1.0.0終于發(fā)布了,我們祝賀Red Hat的整個(gè)團(tuán)隊(duì)在看起來非常有希望的新JVM語言方面所取得的成就。Ceylon與Scala的競爭將是一個(gè)小挑戰(zhàn),但有許多非常有趣的功能使它與眾不同。
無論如何,足夠多的人是誰。這是我希望在Java中獲得的個(gè)人十大Ceylon語言功能列表:
1.模塊
在Java中,Jigsaw已被推遲了約34次,而我們現(xiàn)在才關(guān)閉Java 8 GA!是的,我們有OSGi和Maven,它們?cè)谶\(yùn)行時(shí)(OSGi)或編譯時(shí)(Maven)的依賴關(guān)系管理方面都工作得很好。但是,使用Apache Felix比較這個(gè)黑魔法Maven / OSGi配置…
<plugin> <groupId>org.apache.felix</groupId> <artifactId>maven-bundle-plugin</artifactId> <version>2.1.0</version> <extensions>true</extensions> <executions> <execution> <id>bundle-manifest</id> <phase>process-classes</phase> <goals> <goal>manifest</goal> </goals> </execution> </executions> <configuration> <supportedProjectTypes> <supportedProjectType> jar </supportedProjectType> </supportedProjectTypes> <instructions> <Bundle-SymbolicName> org.jooq </Bundle-SymbolicName> <Export-Package>*</Export-Package> <Import-Package> javax.persistence;resolution:=optional, org.apache.log4j;resolution:=optional, * </Import-Package> <_versionpolicy> [$(version;==;$(@)),$(version;+;$(@))) </_versionpolicy> </instructions> </configuration> </plugin>
最后,可以在jar級(jí)別上控制事物,包括程序包的可見性。僅用幾行代碼。Java,請(qǐng)集成Ceylon的強(qiáng)大模塊支持。
值得一提的是Fantom是另一種具有集成模塊支持的語言。參見JodaTime在2011年Devoxx上的Stephen Colebourne的演講: “ Scala的Fantom光年還遠(yuǎn)嗎?” 。Stephen還為我們帶來了ElSql,這是一種用于Java模板的新外部SQL DSL。
2.序列
這是我第一次看到這種對(duì)類型安全的語言的序列的一流支持。Ceylon不僅附帶各種收集文字,而且還知道這些構(gòu)造的類型。具體來說,您可以這樣聲明一個(gè)Iterable:
<code class="java plain">{String+} words = { "hello", "world" };</code>
注意文字的符號(hào)。類型 {String+}為,表示它至少包含一個(gè)元素。該類型與兼容 {String*},表示可能是空序列。很有意思。
通過這樣支持?jǐn)?shù)組文字來繼續(xù):
String[] operators = [ "+", "-", "*", "/" ]; String? plus = operators[0]; String[] multiplicative = operators[2..3];
…或元組文字:
<code class="java plain">[Float,Float,String] point = [0.0, 0.0, "origin"];</code>
還要注意范圍文字2..3,它允許從原始數(shù)組中提取子數(shù)組。Ceylon有如此多的序列優(yōu)勢!
還要注意中的問號(hào)String?,這是Ceylon聲明…的方式。
3.可空類型
Scala知道Option類型,Haskell知道Maybe類型,而 Java 8試圖通過添加新的,不可執(zhí)行的Optional類型進(jìn)行競爭,但是Ceylon擁有一個(gè)非常簡單的概念,即可以為空的東西。如果類型后面有問號(hào),則它可以為空。否則,它不為null??偸沁@樣。
為了將可為空的類型轉(zhuǎn)換為不可為空的類型,您必須顯式檢查:
void hello() { String? name = process.arguments.first; String greeting; if (exists name) { greeting = "Hello, ``name``!"; } else { greeting = "Hello, World!"; } print(greeting); }
注意exists操作員。它定義了一個(gè)新的范圍,在該范圍內(nèi),該name變量已知不為null,即,將其從提升String?為 String。盧卡斯·里茲(Lukas Rytz)認(rèn)為,這種局部作用域類型的提升通常稱為流敏感類型化,這已經(jīng)在Whiley語言中被觀察到。
如果省略該exists檢查,則在該字符串插值處將出現(xiàn)編譯錯(cuò)誤。還有其他有用的結(jié)構(gòu)可以執(zhí)行臨時(shí)類型轉(zhuǎn)換:
<code class="java plain">String greeting = "Hello, " + (name else "World"); </code>
該else子句的行為類似于SQL COALESCE()函數(shù),甚至可以鏈接起來。閱讀有關(guān)Ceylon的可為善的更多信息 。
4.默認(rèn)參數(shù)
OMG,我希望我們能用Java做到這一點(diǎn)。我們認(rèn)為,每次我們重載方法時(shí),為什么不僅僅支持默認(rèn)參數(shù),例如PL / SQL?
void hello(String name="World") { print("Hello, ``name``!"); }
我無法想到為什么語言不會(huì)具有PL / SQL之類的命名和默認(rèn)參數(shù)的一個(gè)很好的理由:
-- One of the parameters is optional CREATE PROCEDURE MY_PROCEDURE ( P1 IN NUMBER, P2 IN VARCHAR2 := 'ABC', P3 IN VARCHAR2 ); -- Calling the procedure MY_PROCEDURE( P1 => 1, P3 => 'XYZ'
因此,這是在大多數(shù)情況下避免方法重載的一種方法。當(dāng)我們要處理替代的,不兼容的類型時(shí),方法重載仍然很繁瑣。但是Ceylon不知道,但是在Ceylon……
5.聯(lián)合類型
好的,這有點(diǎn)深?yuàn)W。Ceylon的創(chuàng)建者真的很想擺脫方法重載,部分原因是Ceylon也可以編譯為JavaScript,而JavaScript不知道函數(shù)重載。實(shí)際上,根本無法在Ceylon中重載方法。但是,為了能夠與Java進(jìn)行互操作,需要引入聯(lián)合類型。聯(lián)合類型 String|Integer可以是String或Integer。那里有方法重載!
void printType(String|Integer|Float val) { ... } printType("hello"); printType(69); printType(-1.0);
為了“解開”聯(lián)合類型,您可以val通過執(zhí)行類似于Java的類型檢查來再次利用流敏感類型的 參數(shù)instanceof
void printType(String|Integer|Float val) { switch (val) case (is String) { print("String: ``val``"); } case (is Integer) { print("Integer: ``val``"); } case (is Float) { print("Float: ``val``"); } }
例如,在該范圍內(nèi), val編譯器知道該類型 String是。這繼續(xù)允許瘋狂的東西,例如枚舉類型,其中一個(gè)類型可以同時(shí)是一個(gè)或另一個(gè)東西:
abstract class Point() of Polar | Cartesian { // ... }
請(qǐng)注意,這是由多重繼承很大的不同,其中這樣的Point會(huì)都 Polar 和 Cartesian。但這還不是全部。Ceylon也有……
6.交叉點(diǎn)類型
現(xiàn)在,您可能已經(jīng)猜到了,這與聯(lián)合類型的確切含義相反,而Java的泛型實(shí)際上也支持這種類型。在Java中,您可以編寫:
<code class="java keyword">class X> {}</code>
在上面的示例中, X僅接受Serializable 和的 類型參數(shù) Comparable。在Ceylon,這很瘋狂,您可以在其中為本地聲明的交叉點(diǎn)類型分配值。事實(shí)并非如此!在我們的聊天中,Gavin向我指出了這一令人難以置信的語言功能,其中聯(lián)合/交叉點(diǎn)類型可以與流敏感類型交互以形成以下內(nèi)容(由于Ceylon 1.2導(dǎo)致):
value x = X(); //x has type X if (something) { x = Y(); //x has type Y } //x has type X|Y
有道理吧?所以我問他,是否可以再次與該類型相交, Z加文說,是的!可以完成以下操作:
value x = X(); //x has type X if (something) { x = Y(); //x has type Y } //x has type X|Y if (is Z x) { //x has type &Z }
之所以這樣,是因?yàn)轭愋徒患惨苑浅S腥さ姆绞脚c泛型交互。在某些情況下, 可以與相同。換句話說,交集(和并集) 與泛型一起分布,就像加法與乘法一樣(在對(duì)“就像”的非正式理解中)。如果您愿意為此目的研究語言規(guī)范,請(qǐng)參見 §3.7.2主體實(shí)例化繼承。X&X X
現(xiàn)在,聯(lián)合和相交類型會(huì)變得非常討厭并且難以重用。這就是Ceylon擁有……
7.輸入別名
有沒有其他編程語言想到過這個(gè)很棒的功能?即使您不支持聯(lián)合和/或交叉點(diǎn)類型,這也是如此有用??紤]一下Java的泛型。隨著泛型的出現(xiàn),人們開始寫類似以下內(nèi)容的東西:
<code class="java plain">Map>> map = // ...</code>
可以說兩件事:
泛型對(duì)Java庫非常有用
執(zhí)行上述操作時(shí),泛型變得非常冗長
這就是類型別名起作用的地方??纯催@個(gè)例子:
interface People => Set;
這里的要點(diǎn)是,即使某些冗長的類型經(jīng)常被重用,您也不需要為上述內(nèi)容創(chuàng)建顯式的子類型。換句話說,您不想濫用子類型多態(tài)性作為“簡化”通用多態(tài)性的捷徑。將別名視為一個(gè)可擴(kuò)展的宏,該宏可以相互分配兼容。換句話說,您可以編寫:
People? p1 = null; Set? p2 = p1; People? p3 = p2;
因此,正如“別名”一詞所暗示的那樣,您并不是在創(chuàng)建新的類型。您只是給復(fù)雜類型一個(gè)更簡單的名稱。但是比類型別名更好的是……
8.類型推斷
許多其他語言都具有這種功能,Java在一定程度上也是如此,至少就涉及泛型而言。Java 8在允許使用泛型進(jìn)行類型推斷方面又走了一步。但是Java與諸如Scala或Ceylon之類的語言可以使用局部變量相去甚遠(yuǎn):
interface Foo {} interface Bar {} object foobar satisfies Foo&Bar {} //inferred type Basic&Foo&Bar value fb = foobar; //inferred type {Basic&Foo&Bar+} value fbs = { foobar, foobar };
因此,此示例顯示了許多組合的功能,包括類型約束,序列類型,聯(lián)合類型。使用如此豐富的類型系統(tǒng),在value關(guān)鍵字指示您不想(或者您不能)顯式聲明類型的情況下,支持這種類型的推斷非常重要。我真的很想在Java 9中看到這一點(diǎn)!
閱讀有關(guān)Ceylon出色的類型推斷功能的更多信息。
9.申報(bào)地點(diǎn)差異
現(xiàn)在,此功能可能很難理解,因?yàn)镴ava的泛型已經(jīng)非常難以理解。我最近閱讀了Ross Tate,Alan Leung和Sorin Lerner的一篇非常有趣的論文,內(nèi)容涉及通配符給Java泛型帶來的挑戰(zhàn):在Java的Type System中馴服通配符。
泛型仍然是一個(gè)非?;钴S的研究主題,無論是研究人員還是語言設(shè)計(jì)人員都無法完全同意使用站點(diǎn)的差異(如Java)還是聲明位置的差異(如C#,Scala或Ceylon)對(duì)于主流程序員而言確實(shí)更好。談?wù)摬町惖妮^舊語言是Eiffel 和OCaml。
Microsoft已在C#中引入了聲明站點(diǎn)差異。我會(huì)引用Wikipedia中的示例,它很容易理解。在C#中,IEnumerator接口具有協(xié)變泛型類型參數(shù):
interface IEnumerator { T Current { get; } bool MoveNext(); }
這僅意味著以下將起作用:
IEnumerator<Cat> cats = ... IEnumerator<Animal> animals = cats;
這與Java的使用站點(diǎn)差異完全不同,在Java中,上述內(nèi)容無法編譯,但以下內(nèi)容可以:
Iterator cats = ... Iterator<!-- ? extends Animal --> animals = cats;
聲明站點(diǎn)協(xié)方差的主要原因是簡單的事實(shí),即在使用站點(diǎn)上冗長程度大大降低了。通配符是Java開發(fā)人員的主要苦惱,它們引發(fā)了許多Stack Overflow問題,因?yàn)檫@是有關(guān)本地范圍的通配符的問題:
// Given this interface: public interface X { E get(); E set(E e); } // This does not compile: public void foo(X<!--?--> x) { x.set(x.get()); }
在Ceylon語言之旅中可以看到,Ceylon泛型支持聲明站點(diǎn)差異,就像C#和Scala一樣。有趣的是,由于兩種類型的方差支持各有利弊,因此這些事情如何演變,同時(shí) Ross Tate提倡混合站點(diǎn)方差,這對(duì)于Java語言而言確實(shí)是一個(gè)很好的補(bǔ)充!
現(xiàn)在,這有點(diǎn)復(fù)雜,所以讓我們看一下一個(gè)簡單卻很棒的功能來匯總……
10.功能和方法
斯特凡·埃帕多(Stéphaneépardaud)概述的主要內(nèi)容之一是,Ceylon語言是一種非常普通的語言。在考慮Ceylon如何對(duì)待函數(shù)(和方法,它們是類型成員函數(shù))時(shí),這一點(diǎn)尤其明顯 。我可以在任何地方放置函數(shù)??紤]以下示例:
Integer f1() => 1; class C() { shared Integer f2() { Integer f3() => 2; return f3(); } } print(f1()); print(C().f2());
在上面的示例中,
f1()是一個(gè)包級(jí)函數(shù)(非常像Java中的“global”靜態(tài)函數(shù))
f2()是C類中的常規(guī)方法
f3()是f2()方法中的局部函數(shù)
有了Java 8對(duì)lambda表達(dá)式的支持,這些事情會(huì)變得更好一些,但是能夠以幾乎相同的語法在任何地方聲明函數(shù)不是很棒嗎?
結(jié)論:與Ceylon一起玩
現(xiàn)在就這樣。不久之后,我們可能會(huì)發(fā)布有關(guān)Ceylon更深?yuàn)W的語言功能的后續(xù)文章。無論如何,您都可以在Eclipse中通過一流的IDE支持免費(fèi)下載這種有趣的JVM語言。您還可以訪問Ceylon文檔網(wǎng)站,并使他們的網(wǎng)站將Ceylon代碼編譯為JavaScript以在瀏覽器中執(zhí)行。
作者介紹