发布于:2021-02-12 00:00:07
0
259
0
Java 8很好地窃取了Scala的想法,但是这里列出了Java开发人员仍然梦寐以求的Ceylon编程功能。
当Hibernate完成并且功能完善并且需要新的挑战时,该怎么办?对。人们创建了一种新的JVM语言,称为Ceylon。
2013年11月12日,Ceylon 1.0.0终于发布了,我们祝贺Red Hat的整个团队在看起来非常有希望的新JVM语言方面所取得的成就。Ceylon与Scala的竞争将是一个小挑战,但有许多非常有趣的功能使它与众不同。
无论如何,足够多的人是谁。这是我希望在Java中获得的个人十大Ceylon语言功能列表:
1.模块
在Java中,Jigsaw已被推迟了约34次,而我们现在才关闭Java 8 GA!是的,我们有OSGi和Maven,它们在运行时(OSGi)或编译时(Maven)的依赖关系管理方面都工作得很好。但是,使用Apache Felix比较这个黑魔法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级别上控制事物,包括程序包的可见性。仅用几行代码。Java,请集成Ceylon的强大模块支持。
值得一提的是Fantom是另一种具有集成模块支持的语言。参见JodaTime在2011年Devoxx上的Stephen Colebourne的演讲: “ Scala的Fantom光年还远吗?” 。Stephen还为我们带来了ElSql,这是一种用于Java模板的新外部SQL DSL。
2.序列
这是我第一次看到这种对类型安全的语言的序列的一流支持。Ceylon不仅附带各种收集文字,而且还知道这些构造的类型。具体来说,您可以这样声明一个Iterable:
<code class="java plain">{String+} words = { "hello", "world" };</code>
注意文字的符号。类型 {String+}为,表示它至少包含一个元素。该类型与兼容 {String*},表示可能是空序列。很有意思。
通过这样支持数组文字来继续:
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,它允许从原始数组中提取子数组。Ceylon有如此多的序列优势!
还要注意中的问号String?,这是Ceylon声明…的方式。
3.可空类型
Scala知道Option类型,Haskell知道Maybe类型,而 Java 8试图通过添加新的,不可执行的Optional类型进行竞争,但是Ceylon拥有一个非常简单的概念,即可以为空的东西。如果类型后面有问号,则它可以为空。否则,它不为null。总是这样。
为了将可为空的类型转换为不可为空的类型,您必须显式检查:
void hello() { String? name = process.arguments.first; String greeting; if (exists name) { greeting = "Hello, ``name``!"; } else { greeting = "Hello, World!"; } print(greeting); }
注意exists操作员。它定义了一个新的范围,在该范围内,该name变量已知不为null,即,将其从提升String?为 String。卢卡斯·里兹(Lukas Rytz)认为,这种局部作用域类型的提升通常称为流敏感类型化,这已经在Whiley语言中被观察到。
如果省略该exists检查,则在该字符串插值处将出现编译错误。还有其他有用的结构可以执行临时类型转换:
<code class="java plain">String greeting = "Hello, " + (name else "World"); </code>
该else子句的行为类似于SQL COALESCE()函数,甚至可以链接起来。阅读有关Ceylon的可为善的更多信息 。
4.默认参数
OMG,我希望我们能用Java做到这一点。我们认为,每次我们重载方法时,为什么不仅仅支持默认参数,例如PL / SQL?
void hello(String name="World") { print("Hello, ``name``!"); }
我无法想到为什么语言不会具有PL / SQL之类的命名和默认参数的一个很好的理由:
-- 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'
因此,这是在大多数情况下避免方法重载的一种方法。当我们要处理替代的,不兼容的类型时,方法重载仍然很繁琐。但是Ceylon不知道,但是在Ceylon……
5.联合类型
好的,这有点深奥。Ceylon的创建者真的很想摆脱方法重载,部分原因是Ceylon也可以编译为JavaScript,而JavaScript不知道函数重载。实际上,根本无法在Ceylon中重载方法。但是,为了能够与Java进行互操作,需要引入联合类型。联合类型 String|Integer可以是String或Integer。那里有方法重载!
void printType(String|Integer|Float val) { ... } printType("hello"); printType(69); printType(-1.0);
为了“解开”联合类型,您可以val通过执行类似于Java的类型检查来再次利用流敏感类型的 参数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``"); } }
例如,在该范围内, val编译器知道该类型 String是。这继续允许疯狂的东西,例如枚举类型,其中一个类型可以同时是一个或另一个东西:
abstract class Point() of Polar | Cartesian { // ... }
请注意,这是由多重继承很大的不同,其中这样的Point会都 Polar 和 Cartesian。但这还不是全部。Ceylon也有……
6.交叉点类型
现在,您可能已经猜到了,这与联合类型的确切含义相反,而Java的泛型实际上也支持这种类型。在Java中,您可以编写:
<code class="java keyword">class X> {}</code>
在上面的示例中, X仅接受Serializable 和的 类型参数 Comparable。在Ceylon,这很疯狂,您可以在其中为本地声明的交叉点类型分配值。事实并非如此!在我们的聊天中,Gavin向我指出了这一令人难以置信的语言功能,其中联合/交叉点类型可以与流敏感类型交互以形成以下内容(由于Ceylon 1.2导致):
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 }
之所以这样,是因为类型交集也以非常有趣的方式与泛型交互。在某些情况下, 可以与相同。换句话说,交集(和并集) 与泛型一起分布,就像加法与乘法一样(在对“就像”的非正式理解中)。如果您愿意为此目的研究语言规范,请参见 §3.7.2主体实例化继承。X&X X
现在,联合和相交类型会变得非常讨厌并且难以重用。这就是Ceylon拥有……
7.输入别名
有没有其他编程语言想到过这个很棒的功能?即使您不支持联合和/或交叉点类型,这也是如此有用。考虑一下Java的泛型。随着泛型的出现,人们开始写类似以下内容的东西:
<code class="java plain">Map>> map = // ...</code>
可以说两件事:
泛型对Java库非常有用
执行上述操作时,泛型变得非常冗长
这就是类型别名起作用的地方。看看这个例子:
interface People => Set;
这里的要点是,即使某些冗长的类型经常被重用,您也不需要为上述内容创建显式的子类型。换句话说,您不想滥用子类型多态性作为“简化”通用多态性的捷径。将别名视为一个可扩展的宏,该宏可以相互分配兼容。换句话说,您可以编写:
People? p1 = null; Set? p2 = p1; People? p3 = p2;
因此,正如“别名”一词所暗示的那样,您并不是在创建新的类型。您只是给复杂类型一个更简单的名称。但是比类型别名更好的是……
8.类型推断
许多其他语言都具有这种功能,Java在一定程度上也是如此,至少就涉及泛型而言。Java 8在允许使用泛型进行类型推断方面又走了一步。但是Java与诸如Scala或Ceylon之类的语言可以使用局部变量相去甚远:
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 };
因此,此示例显示了许多组合的功能,包括类型约束,序列类型,联合类型。使用如此丰富的类型系统,在value关键字指示您不想(或者您不能)显式声明类型的情况下,支持这种类型的推断非常重要。我真的很想在Java 9中看到这一点!
阅读有关Ceylon出色的类型推断功能的更多信息。
9.申报地点差异
现在,此功能可能很难理解,因为Java的泛型已经非常难以理解。我最近阅读了Ross Tate,Alan Leung和Sorin Lerner的一篇非常有趣的论文,内容涉及通配符给Java泛型带来的挑战:在Java的Type System中驯服通配符。
泛型仍然是一个非常活跃的研究主题,无论是研究人员还是语言设计人员都无法完全同意使用站点的差异(如Java)还是声明位置的差异(如C#,Scala或Ceylon)对于主流程序员而言确实更好。谈论差异的较旧语言是Eiffel 和OCaml。
Microsoft已在C#中引入了声明站点差异。我会引用Wikipedia中的示例,它很容易理解。在C#中,IEnumerator接口具有协变泛型类型参数:
interface IEnumerator { T Current { get; } bool MoveNext(); }
这仅意味着以下将起作用:
IEnumerator<Cat> cats = ... IEnumerator<Animal> animals = cats;
这与Java的使用站点差异完全不同,在Java中,上述内容无法编译,但以下内容可以:
Iterator cats = ... Iterator<!-- ? extends Animal --> animals = cats;
声明站点协方差的主要原因是简单的事实,即在使用站点上冗长程度大大降低了。通配符是Java开发人员的主要苦恼,它们引发了许多Stack Overflow问题,因为这是有关本地范围的通配符的问题:
// 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泛型支持声明站点差异,就像C#和Scala一样。有趣的是,由于两种类型的方差支持各有利弊,因此这些事情如何演变,同时 Ross Tate提倡混合站点方差,这对于Java语言而言确实是一个很好的补充!
现在,这有点复杂,所以让我们看一下一个简单却很棒的功能来汇总……
10.功能和方法
斯特凡·埃帕多(StéphaneÉpardaud)概述的主要内容之一是,Ceylon语言是一种非常普通的语言。在考虑Ceylon如何对待函数(和方法,它们是类型成员函数)时,这一点尤其明显 。我可以在任何地方放置函数。考虑以下示例:
Integer f1() => 1; class C() { shared Integer f2() { Integer f3() => 2; return f3(); } } print(f1()); print(C().f2());
在上面的示例中,
f1()是一个包级函数(非常像Java中的“global”静态函数)
f2()是C类中的常规方法
f3()是f2()方法中的局部函数
有了Java 8对lambda表达式的支持,这些事情会变得更好一些,但是能够以几乎相同的语法在任何地方声明函数不是很棒吗?
结论:与Ceylon一起玩
现在就这样。不久之后,我们可能会发布有关Ceylon更深奥的语言功能的后续文章。无论如何,您都可以在Eclipse中通过一流的IDE支持免费下载这种有趣的JVM语言。您还可以访问Ceylon文档网站,并使他们的网站将Ceylon代码编译为JavaScript以在浏览器中执行。
作者介绍