再来一次。 那个话题 。
但是等一下 您每天都不会看到这里讨论的方法(以及用锡兰语言)。 同时,它非常狡猾。
空值被烤成语言
…大概看起来。 确实,在锡兰,就像在Kotlin (以及可能的许多其他语言)中一样,有一个特殊的“注释”类型,您可以将其后缀为任何引用类型,以使其可为空。 例如:
String firstName = "Homer";
String? middleName = "J";
String lastName = "Simpson";
在上面的示例中, firstName
和lastName
都是强制性值,永远不能为null
,而middleName
是可选的。 然后,大多数支持上述内容的语言都会附带特殊的运算符以访问可选值,例如.?
在锡兰和Kotlin 。
// Another optional value:
Integer? length = middleName.?length;
// A non-optional value:
Integer length = middleName.?length else 0;
那么,锡兰如此平稳地工作又是什么呢?
锡兰说得很对的事情是,以上所有都是语法糖,它是:
- 易于使用
- 很好地映射了我们的思维方式,即
null
仍然是一个问题 - 可以与Java互操作
- 不引入认知摩擦
对于我们Java的人来说,我们仍然可以假装null
是一种可以避免的难事( 就像我们在此Blog之前所声称的那样 )。 但是,实际上什么是 null
? 是缺少价值吗? 未知值? 未初始化的值?
Java只有一个null
的事物,它被所有先前的事物所用(滥用),并且从理论上讲,它实际上只是未初始化的值,仅此而已。 另一方面,当使用JDBC(因此使用SQL)时,它隐式表示未知值( 带有所有相关的警告 )。
但是在Ceylon中, Null
是一种特殊类型 ,类似于Java中的Void
。 可以分配给Null
类型的唯一值是null
:
// Ceylon
Null x = null;
// Java
Void x = null;
但是最大的区别是,不能将null
分配给任何其他类型! 等待。 我们不能将null
分配给String?
……? 当然,在锡兰,可能会发生以下情况:
String? x = null;
但是为什么这可能呢? 因为String?
只是String|Null
( 联合类型)的语法糖,即,它是String
类型还是Null
类型。
呵呵,工会类型是什么?
让我们更仔细地看一下。 在jOOQ API中,当您想使用SQL函数和表达式时,总是会有大量的重载为您提供标准版本和便捷的版本,您可以在其中传递绑定变量。 以equals运算符为例:
interface Field<T> {
Condition eq(Field<T> field);
Condition eq(T value);
}
上面的重载使您可以编写如下内容,而无需考虑SQL表达式和Java绑定变量(最终还是SQL表达式)之间的区别:
// Comparing a column with bind variable
.where(BOOK.ID.eq(1))
// Comparing a column with another column expression
.and(BOOK.AUTHOR_ID.eq(AUTHOR.ID))
实际上,还有更多的重载,因为比较操作的右侧也可以具有其他表达式,例如:
interface Field<T> {
Condition eq(Field<T> field);
Condition eq(T value);
Condition eq(Select<? extends Record1<T>> query);
Condition eq(QuantifiedSelect<? extends Record1<T>> query);
}
现在,需要重复相同的一组重载,而不是等于,大于,大于或等于,等等。将这种“右侧”东西表达为单个可重用类型不是很好吗? ? 即上述所有类型的联合类型?
interface Field<T> {
Condition eq(
Field<T>
| T
| Select<? extends Record1<T>>
| QuantifiedSelect<? extends Record1<T>> thingy
);
}
甚至
// This is called a type alias. Another awesome
// Ceylon language feature (pseudo syntax)
alias Thingy =>
Field<T>
| T
| Select<? extends Record1<T>>
| QuantifiedSelect<? extends Record1<T>>;
interface Field<T> {
Condition eq(Thingy thingy);
}
毕竟,这也是SQL语言的定义方式。 哎呀,这就是任何BNF表示法定义语法元素的方式。 例如:
<predicate> ::=
<comparison predicate>
| <between predicate>
| <in predicate>
| <like predicate>
| <null predicate>
| <quantified comparison predicate>
| <exists predicate>
| <unique predicate>
| <match predicate>
| <overlaps predicate>
好吧,理所当然,句法元素与类型并不完全相同,但直觉却是相同的。
哦,Java也有联合类型!
简短的启示是,Java 7专家组在异常处理中添加了对联合类型的支持。 您可以编写如下内容:
try {
...
}
catch (IOException | SQLException e) {
// e can be any of the above!
}
返回锡兰和空
锡兰正确地使Null
。 因为从历史上看,可空类型是可以是“实”类型或“空”值的类型。 我们想要那个。 我们Java开发人员渴望这一点。 没有这种选择的舒缓选择,我们就无法生存。
但是这种方法的优点在于它是可扩展的。 如果我真的需要区分“未知”,“未初始化”,“未定义”,“ 42”怎么办? 我可以。 使用类型。 这是一个可以对所有上述“特殊值”进行建模的字符串:
String|Unknown|Uninitialised|Undefined|FortyTwo
如果太冗长,我只给它起一个名字
interface TheStringToRuleThemAll
=> String|Unknown|Uninitialised|Undefined|FortyTwo;
但这不能为Null
。 因为我不想让它成为那样的价值,所以那就是一切,一无所有。 你说服了吗? 我敢打赌,你是。 从现在开始:
不要相信任何假装Option(al)monad是对null建模的体面方法的语言。 不是。
- 我。 现在
为什么? 让我举例说明。 使用elvis运算符的Kotlin / Ceylon / Groovy样式语法糖(无论支持空null语义如何):
String name = bob?.department?.head?.name
与Optional
单子相同:
Optional<String> name = bob
.flatMap(Person::getDepartment)
.map(Department::getHead)
.flatMap(Person::getName);
而且,如果您一次将map()与flatMap()混合在一起,就会很可怜!
“ @EmrgencyKittens :盒子里的猫,盒子里的猫。 pic.twitter.com/ta976gqiQs ”而且我认为flatMap
— Channing Walton(@channingwalton) 2014年3月23日
有人声称:
使用工会类型就像在您婆婆坐在副驾驶席上驾驶崭新的法拉利那样。
当然。 但我声称:做得好,锡兰。 希望我们也可以在catch块之外获得Java中的联合类型!
翻译自: https://www.javacodegeeks.com/2016/03/ceylon-might-just-jvm-language-got-nulls-right.html