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