title | date | author | tags | keywords | categories | reward | reward_title | reward_wechat | reward_alipay | source_url |
---|---|---|---|---|---|---|---|---|---|---|
[译]Call for Feedback: Upcoming Changes in Kotlin |
2015-09-18 07:06:00 -0700 |
Andrey Breslav |
官方动态 |
false |
Have a nice Kotlin! |
如前所述,我们正在使用语言设计,这篇文章是即将到来的更改的主要内容+请求您的反馈。 </ span>
之前我提到我们对现在的支持字段语法感到不满,这是 $ propertyName </ code>:
{% raw %}
{% endraw %}
var foo: Foo = ...
get() { beforeRead(); return $foo }
set(v) { beforeWrite($foo, v); $foo = v }
{% raw %}
{% endraw %}
最大的问题是这个语法与语法相冲突 字符串模板 。
所以,我们决定在这里改变规则:
- $ foo语法将被弃用,然后删除
- 相反,我们可以通过getters / setters中的名称字段访问支持字段:
{% raw %}
{% endraw %}
var foo: Foo = ...
get() { beforeRead(); return field }
set(v) { beforeWrite(field, v); field = v }
{% raw %}
{% endraw %}
请注意,字段</ code>只是一个隐式定义的变量(非常类似于lambdas中的 it </ code>)。
这种方法不支持一些用例:我们以前可以在类中的任何位置访问支持字段,现在只能在getter / setter中显示。我们已经在GitHub上检查了Kotlin代码,并且意识到只有很小一部分的用例没有被覆盖,对于这些,我们可以随时使用“支持属性”:
{% raw %}
{% endraw %}
private var _foo = ...
public var foo: Foo
get() = ...
set(v) { ... }
{% raw %}
{% endraw %}
过去已经有很多争议了,我们终于决定要在Kotlin中介绍一下操作符重载和中缀函数调用的方法。目前可以称为 a.plus(b)</ code>的任何名为 plus </ code>的函数可以称为 a + b </ code>。我们将要求这些函数用运算符</ code>修饰符标记,否则运算符符号将不可用。这使得操作员使用更加规范,并消除了随机标点符号侵入API的可能性。最常见的例子是使用一个名为 get </ code>的方法,但完全不打算用作方括号。
对于中缀函数调用是一样的:我们需要一个函数被标记为中缀</ code>。这将减少通用API中不寻常的样式多样性:
- list add 1 vs list.add(1)
- 列表地图{...} vs list.map {...}
- 等等
Infix函数仍然可以使用旧的标准语法 x.or(y)</ code>进行调用,但是这个工具将会暗示你的语法是中缀。
请注意,标准库中的常见功能(例如 map </ code>或 filter </ code>)不会被标记为中缀</ code>,因为使用它们有时会导致隐藏如果这样一个表达式后面是一个点,则会出现错误:
{% raw %}
{% endraw %}
list map {...}.toSet() // Error: toSet() is not applicable to a lambda
{% raw %}
{% endraw %}
如果某些Java方法没有标记为 operator </ code>或 infix </ code>,我们可以随时定义一个扩展名,标准库将为大多数流行的案例提供此类扩展。
对于注解,编译时常数很重要:只有它们可以用作参数(以及很少的额外表达式,即数组和注释构造函数)。到目前为止,我们采用相同的“隐含”方法来检测它们,如Java所示:如果对象</ code>或顶层的 val </ code>在其初始化程序中只有常量,它是一个编译时常数。这是脆弱的,并提出了破解API的可能性,而不知道,所以我们决定要求这样的 val </ code>的 const </ code>修饰符:
{% raw %}
{% endraw %}
const val SCREEN_WIDTH = 2048
{% raw %}
{% endraw %}
注意: const </ code>值只能有以下类型:“primitives”, String </ code>,枚举,类文字。
到目前为止,这已经非常模糊,但是我们将会改变它。现在,如果一个值需要作为扩展函数调用,它必须有一个成员,它是一个扩展名,并被命名为 invoke </ code>:
{% raw %}
{% endraw %}
class Foo {
operator fun String.invoke() { ... }
}
fun test(foo: Foo) {
"".foo()
}
{% raw %}
{% endraw %}
这在某些情况下是不方便的,所以我们要将其更改为
{% raw %}
{% endraw %}
class Foo {
operator fun invokeExtension(s: String) { ... }
}
fun test(foo: Foo) {
"".foo()
}
{% raw %}
{% endraw %}
作为副作用,可以添加如扩展名的功能:
{% raw %}
{% endraw %}
class Foo
operator fun Foo.invokeExtension(s: String) { ... }
{% raw %}
{% endraw %}
内部成员被编译为 public </ code>,这可能会导致意外覆盖:
{% raw %}
{% endraw %}
// module X
open class Base {
internal fun foo() {...}
}
// module Y
class Derived : Base() {
fun foo() {...}
}
{% raw %}
{% endraw %}
由于父函数不可见,编译器不需要 Derived :: foo </ code>中的 override </ code>,而是在字节码中具有相同的签名,并且运行时将绑定它们作为覆盖,这不是作者的意图。当模块X和Y独立演进(例如,一个是库和另一个用户的项目)时,问题最为困难,因此,当Y编译时, foo </ code>尚未出现在 X < / code>。
为了避免这种情况,我们决定修改内部成员的名称,以免与超类成员冲突。
更新</ strong>:调整可能会导致此成员无法从Java调用。这似乎很难解决,但解决方法很简单:只需使其 public </ code>或 protected </ code>即可。
- Java 6上接口的默认实现类将被命名为Foo.DefaultImpls而不是Foo $$ TImpl
- _,__,___将被禁止作为标识符,即我们可以使用_foo,但不能单独使用(保留供将来使用)
- 我们将在界面中删除最终的,受保护的和内部的:这些不能在JVM上表达,所以我们推迟他们的实现,直到后来
- 我们要放弃identityEquals()函数,有利于===
您的意见和用例是最受欢迎的!