当Hibernate“完成”并且功能完善并且需要新的挑战时,该怎么办? 对。 人们创建了一种新的JVM语言,称为Ceylon 。
2013年11月12日, Ceylon 1.0.0终于发布了 ,我们祝贺Red Hat的整个团队在看起来非常有希望的新JVM语言方面所取得的成就。 锡兰与Scala的竞争将是一个小挑战,但是有许多非常有趣的功能使它与众不同。
实际上,这种语言具有许多有趣的功能,因此很难撰写有关10个最有趣的功能的博客文章。 选择哪一个? 在Google Plus上,我与加文·金(Gavin King)进行了简短的交谈,加文·金( Kavin King)也带来了我们的冬眠 ; 罗斯·泰特 ( Ross Tate )也参与了JetBrains的Kotlin的研究 ; 卢卡斯·里兹 ( Lukas Rytz )是EPFL Scala的博士研究生和提交人,现在在Google Dart工作 。 我希望那些语言Uberdesigners可以帮助我找到他们拥有的10种最激动人心的语言功能,而我们的Java开发人员则没有。 现在我有20个有趣的。 我一定会为此写一篇后续文章。
我观察到加文·金(Gavin King)和其他人非常热情和知识渊博。 在我于2013年2月在瑞士伯尔尼的 JUGS上的StéphaneÉpardaud第一次听说锡兰时,我就已经有了这种印象,他是RedHat另一位热情的工程师( 请参阅此处的演示幻灯片 )。
无论如何,足够多的人是谁。 这是我希望在Java中获得的个人十大锡兰语言功能列表:
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>
……锡兰的这幅画:
"The second best ever ORM solution!"
license "http://www.gnu.org/licenses/lgpl.html"
module org.hibernate "3.0.0.beta" {
import ceylon.collection "1.0.0";
import java.base "7";
shared import java.jdbc "7";
}
最后,可以在jar级别上控制事物,包括程序包的可见性。 仅用几行代码。 Java,请集成Ceylon的强大模块支持 。
值得一提的是Fantom是另一种具有集成模块支持的语言。 参见JodaTime在2011年Devoxx上的Stephen Colebourne的演讲: “ Scala的Fantom光年还 远 吗?” 。 Stephen还为我们带来了ElSql,这是一种用于Java模板的新外部SQL DSL。
2.顺序
这是我第一次看到这种对类型安全语言的序列的一流支持。 锡兰不仅提供了各种收集文字,而且还知道这些构造的类型。 具体来说,您可以这样声明一个Iterable:
{String+} words = { "hello", "world" };
注意文字的符号。 它的类型为{String+}
,表示它至少包含一个元素。 该类型与{String*}
赋值兼容,后者表示可能为空的序列。 很有意思。
通过这样支持数组文字来继续:
String[] operators = [ "+", "-", "*", "/" ];
String? plus = operators[0];
String[] multiplicative = operators[2..3];
…或元组文字:
[Float,Float,String] point = [0.0, 0.0, "origin"];
还要注意范围文字2..3
,它允许从原始数组中提取子数组。 锡兰有如此多的序列优势!
还要注意String?
问号String?
,这是锡兰宣布…的方式
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?
到String
。 卢卡斯·里兹(Lukas Rytz)认为,这种局部作用域类型的提升通常称为流敏感类型化,这已在Whiley语言中被观察到。
如果您省略exists
检查,那么在那里的字符串内插会出现编译错误。 还有其他有用的构造可以执行临时类型转换:
String greeting = "Hello, " + (name else "World");
else
子句的行为类似于SQL COALESCE()
函数,甚至可以链接。 阅读有关锡兰的可为空的善意的更多信息。
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'
);
因此,这是在大多数情况下避免方法重载的一种方法。 当我们要处理替代的,不兼容的类型时,方法重载仍然很繁琐。 但是锡兰不知道,在锡兰不是……
5.联合类型
好的,这有点深奥。 Ceylon的创建者真的很想摆脱方法重载,部分原因是Ceylon也可以编译为JavaScript ,而JavaScript不知道函数重载。 实际上,根本无法在Ceylon中重载方法。 为了能够与Java互操作,需要引入联合类型。 联合类型String|Integer
可以是String或Integer。 那里有方法重载!
void printType(String|Integer|Float val) { ... }
printType("hello");
printType(69);
printType(-1.0);
为了“解开”联合类型,您可以通过执行类似于Java的instanceof
类型检查来再次利用val
参数的流敏感类型
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
。 但这还不是全部。 锡兰也有……
6.交叉点类型
现在,您可能已经猜到了,这是联合类型的确切逆,而Java的泛型实际上也支持这种类型。 在Java中,您可以编写:
class X<E extends Serializable & Comparable<E>> {}
在上面的示例中, X
仅接受Serializable
和 Comparable
类型参数。 在锡兰,这很疯狂,您可以在其中为本地声明的交叉点类型分配值。 事实并非如此! 在我们的聊天中,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 <X|Y>&Z
}
之所以这样,是因为类型交集也以非常有趣的方式与泛型交互。 在某些情况下, X<A>&X<B>
可以与X<A&B>
。 换句话说,交集(和并集)与泛型一起分布 ,就像加法与乘法一样(在对“就像”的非正式理解中)。 如果您愿意为此目的研究语言规范,请参见§3.7.2主体实例化继承 。
现在,联合和相交类型会变得非常讨厌并且难以重用。 这就是锡兰拥有……
7.输入别名
有没有其他编程语言想到过这个很棒的功能? 即使您不支持联合和/或交叉点类型,这也是如此有用。 考虑一下Java的泛型。 随着泛型的出现,人们开始写类似以下内容的东西:
Map<String, List<Map<Integer, String>>> map = // ...
可以说两件事:
- 泛型对于Java库非常有用
- 执行上述操作时,泛型变得非常冗长
这就是类型别名起作用的地方。 看看这个例子:
interface People => Set<Person>;
这里的要点是,即使某些冗长的类型经常被重用,您也不需要为上述内容创建显式的子类型 。 换句话说,您不想滥用子类型多态性作为“简化”通用多态性的捷径 。
可以将别名视为一个可扩展的宏,该宏可以相互分配兼容。 换句话说,您可以编写:
People? p1 = null;
Set<Person>? 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<out T>
{
T Current { get; }
bool MoveNext();
}
这只是意味着以下将起作用:
IEnumerator<Cat> cats = ...
IEnumerator<Animal> animals = cats;
这与Java的使用站点差异完全不同,在Java中,上述内容无法编译,但以下内容可以编译:
Iterator<Cat> cats = ...
Iterator<? extends Animal> animals = cats;
声明站点协方差的主要原因是简单的事实,即在使用站点上冗长程度大大降低了。 通配符是Java开发人员的主要苦恼,它们引发了许多Stack Overflow问题,因为这是有关本地范围的通配符的问题:
// Given this interface:
public interface X<E> {
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如何对待函数(和方法,它们是类型成员函数)时,这一点尤其明显。 我可以在任何地方放置函数。 考虑以下示例:
Integer f1() => 1;
class C() {
shared Integer f2() {
Integer f3() => 2;
return f3();
}
}
print(f1());
print(C().f2());
在以上示例中,
-
f1()
是包级函数(非常类似于Java中的“全局”静态函数) -
f2()
是C
类的常规方法 -
f3()
是f2()
方法中的局部函数
有了Java 8对lambda表达式的支持,这些事情会变得更好一些,但是能够以几乎相同的语法在任何地方声明函数不是很棒吗?
结论:与锡兰一起玩
现在就这样。 不久之后,我们可能会发布有关锡兰更深奥的语言功能的后续文章。 无论如何,您都可以在Eclipse中通过一流的IDE支持免费下载这种有趣的JVM语言。 您还可以访问Ceylon文档网站,并使他们的网站将Ceylon代码编译为JavaScript以在浏览器中执行。
访问社区并与RedHat和Serli的语言设计师进行互动,完成后,在我们的jOOQ博客上分享此帖子,并帮助JCP认识到这种美妙的语言具有Java 9的两个非常有趣的功能或10个路线图!
翻译自: https://www.javacodegeeks.com/2013/12/top-10-ceylon-language-features-i-wish-we-had-in-java.html