Skip to content
This repository has been archived by the owner on Apr 13, 2023. It is now read-only.

Latest commit

 

History

History
214 lines (164 loc) · 6.19 KB

Call for Feedback Upcoming Changes in Kotlin.md

File metadata and controls

214 lines (164 loc) · 6.19 KB
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)进行调用,但是这个工具将会暗示你的语法是中缀。 请注意,标准库中的常见功能(例如mapfilter)不会被标记为中缀,因为使用它们有时会导致隐藏如果这样一个表达式后面是一个点,则会出现错误:

{% raw %}

{% endraw %}
list map {...}.toSet() // Error: toSet() is not applicable to a lambda

{% raw %}

{% endraw %}

如果某些 Java 方法没有标记为operatorinfix,我们可以随时定义一个扩展名,标准库将为大多数流行的案例提供此类扩展。

常数

对于注解,编译时常数很重要:只有它们可以用作参数(以及很少的额外表达式,即数组和注释构造函数)。到目前为止,我们采用相同的“隐含”方法来检测它们,如 Java 所示:如果对象或顶层的val在其初始化程序中只有常量,它是一个编译时常数。这是脆弱的,并提出了破解 API 的可能性,而不知道,所以我们决定要求这样的valconst修饰符:

{% raw %}

{% endraw %}
const val SCREEN_WIDTH = 2048

{% raw %}

{% endraw %}

注意:const值只能有以下类型:“primitives”,String,枚举,类文字。

invokeExtension()约定

到目前为止,这已经非常模糊,但是我们将会改变它。现在,如果一个值需要作为扩展函数调用,它必须有一个成员,它是一个扩展名,并被命名为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 调用。这似乎很难解决,但解决方法很简单:只需使其publicprotected`即可。

其他变化

  • Java 6 上接口的默认实现类将被命名为 Foo.DefaultImpls 而不是 Foo $$ TImpl
  • _,__,___将被禁止作为标识符,即我们可以使用_foo,但不能单独使用(保留供将来使用)
  • 我们将在界面中删除最终的,受保护的和内部的:这些不能在 JVM 上表达,所以我们推迟他们的实现,直到后来
  • 我们要放弃 identityEquals()函数,有利于 ===

反馈

您的意见和用例是最受欢迎的!