title | date | author | tags | keywords | categories | reward | reward_title | reward_wechat | reward_alipay | source_url | translator | translator_url |
---|---|---|---|---|---|---|---|---|---|---|---|---|
[译]M10 is out |
2014-12-17 07:02:00 -0800 |
Hadi Hariri |
官方动态 |
false |
Have a nice Kotlin! |
在庆祝活动开始之前,我们设法发布了 Kotlin 的下一个里程碑,添加了动态类型等等。我们来看看 M10 带给我们什么。
语言的一些改进,特别是:
在 M10 之前,我们有时会写这样的代码:
{% raw %}
{% endraw %}fun <T> TreeNode.findParentOfType(clazz: Class<T>): T? {
var p = parent
while (p != null && !clazz.isInstance(p)) {
p = p?.parent
}
return p as T
}
{% raw %}
{% endraw %}在这里,我们走一棵树,并使用反射来检查节点是否具有某种类型。这一切都很好,但通话网站看起来有点混乱:
{% raw %}
{% endraw %}myTree.findParentOfType(javaClass<MyTreeNodeType>())
{% raw %}
{% endraw %}我们实际想要的是简单地将类型*传递给此功能,即调用如下:
{% raw %}
{% endraw %}myTree.findParentOfType<MyTreeNodeType>()
{% raw %}
{% endraw %}但是,我们需要使用的泛化泛型来访问一个函数中的类型,而在 JVM 上,通用的泛型是昂贵的... 幸运的是,Kotlin 有 内联函数 现在他们支持reified类型参数,所以我们可以这样写:
{% raw %}
{% endraw %}inline fun <reified T> TreeNode.findParentOfType(): T? {
var p = parent
while (p != null && p !is T) {
p = p?.parent
}
return p as T
}
{% raw %}
{% endraw %}我们使用reified修饰符对 type 参数进行限定,现在可以在函数内部访问,几乎就像是普通类一样。由于函数是内联的,因此不需要反射,像**!**这样的正常运算符正在工作。另外,我们可以如上所述调用它:myTree.findParentOfType&MyTreeNodeType&gt;()
。
虽然在许多情况下可能并不需要反思,但我们仍然可以使用一个重定义的类型参数:javaClass <t>()</t>
使我们可以访问它:
{% raw %}
{% endraw %}inline fun methodsOf<reified T>() = javaClass<T>().getMethods()
fun main(s: Array<String>) {
println(methodsOf<String>().joinToString("\n"))
}
{% raw %}
{% endraw %}正常功能(未标记为inline
)不能有参数。不具有运行时表示形式的类型(例如,非引用类型参数或类似于Nothing
的虚构类型)不能用作引用类型参数的参数。
此功能旨在简化传统上依赖于反思的框架中的代码,我们的内部实验表明它的工作正常。
Kotlin 有 申报地点差异 从一开始,但编译器长时间缺少通讯员检查。现在他们放在他们的位置:编译器抱怨如果我们在或out中声明一个类型参数为,但在类体中滥用它:
{% raw %}
{% endraw %}class C<out T> {
fun foo(t: T) {} // ERROR
}
{% raw %}
{% endraw %}在这个例子中,由于 T 在 T 中被声明为out(即类是covariant),我们不允许将它作为foo 的参数()
函数,我们只能返回它。
请注意,私人声明可以违反方差限制,例如:
{% raw %}
{% endraw %}class C<out T>(t: T) {
private var foo: T = t
}
{% raw %}
{% endraw %}虽然foo
的设置器将 T 作为参数,因此违反了out限制,编译器允许这样做,并确保只有同一个实例* C
可以访问foo
。这意味着C
中的以下函数将无法编译:
{% raw %}
{% endraw %}// inside the class C
private fun copyTo(other: C<T>) {
other.foo = foo // ERROR: Can not access foo in another instance of C
}
{% raw %}
{% endraw %}这是一个破坏变化:以前编译的一些代码可能会中断,但是不能修复它可能会导致运行时异常,所以编译器错误将对你有一些价值<img alt =“:)”class =“wp-smiley”data-recalc-dims =“1”src =“https://i2.wp.com/blog.jetbrains.com/kotlin/wp-includes/images/smilies/ simple-smile.png?w = 640&amp; ssl = 1“style =”height:1em; max-height:1em“
类型参数推断已被改进以适应 使用方差 更舒适现在您可以调用通用功能,例如一个投影类型的reverseInPlace()
,例如Array&lt; out Number&gt;
:
{% raw %}
{% endraw %}fun example(a: Array<out Number>) {
a.reverseInPlace()
}
{% raw %}
{% endraw %}其中reverseInPlace
定义如下:
{% raw %}
{% endraw %}fun <T> Array<T>.reverseInPlace() {
fun (i in 0..size() / 2) {
val tmp = this[i]
this[i] = this[size - i - 1]
this[size - i - 1] = tmp
}
}
{% raw %}
{% endraw %}最初由罗斯·泰特(Ross Tate)提出的基本机制 “混合场地差异” 。
另一个突破性变化的形式是对一个晦涩的修复,但有时候 相反 烦人的 问题:当我们有一个使用String?
的变量的函数时,我们真的希望能够将String
的数组传递给它,不是吗?在 M10 之前是不可能的,因为 T 的 vararg 被编译为Array&lt; T&gt;
,现在它们被编译为Array&lt; out T&gt;
,并且以下代码工作:
{% raw %}
{% endraw %}fun takeVararg(vararg strings: String?) { ... }
val strs = array("a", "b", "c")
takeVararg(*strs)
{% raw %}
{% endraw %}JavaScript 在此版本中获得重要更新,支持动态类型。
有时与动态语言交谈的最佳方式是动态的。这就是为什么我们引入了软键盘动态,这样我们可以将类型声明为动态的。目前,这仅在定位 JavaScript 而不是 JVM 时才受支持。 当与 JavaScript 进行互操作时,我们现在可以使用函数作为参数,或返回动态类型
{% raw %}
{% endraw %}fun interopJS(obj: dynamic): dynamic {
...
return "any type"
}
{% raw %}
{% endraw %}我们将在单独的博客文章中详细介绍动态更多细节以及使用场景和限制。对于技术性见 规格文件 。
我们添加了一系列注释,使 JavaScript 互操作更简单,特别是 nativeInvoke,nativeGetter和nativeSetter*。
如果使用nativeInvoke
注释了一个函数bar
,它的调用foo.bar()
将被转换为foo()
在 JavaScript 中。例如:
{% raw %}
{% endraw %}class Foo {
nativeInvoke
fun invoke(a: Int) {}
}
val f = Foo()
f(1) // translates to f(1), not f.invoke(1)
f.invoke(1) // also translates to f(1)
{% raw %}
{% endraw %}同样的方式,我们可以使用 NativeGetter和nativeSetter*来获取 JavaScript 中的索引访问权限:
{% raw %}
{% endraw %}native("Array")
class JsArray<T> {
nativeGetter
fun get(i: Int): T = noImpl
nativeSetter
fun set(i: Int, v: T): Unit = noImpl
}
{% raw %}
{% endraw %}没有native *
注解,调用get
和set
(包括按惯例完成的那些,例如a [i] = j <代码>与
a.set(i,j)相同)被翻译成
a.get(...)和
a.set(.. 。)`,但是注释如上所述,它们在 JavaScript 中被转换为方括号运算符:
{% raw %}
{% endraw %}a[0] // translates to a[0], not a.get(0)
a.get("first") // translates to a["first"]
a[2] = 3 // translates to a[2] = 3
{% raw %}
{% endraw %}在以下情况下,我们可以使用这些注释:
- 非扩展成员函数的本机声明,
- 顶级扩展功能。
以前,在创建新项目时,将在名为scripts的文件夹中创建 kotlin.js 运行时。从 M10 开始,这个文件是在第一次编译时创建的,并且被放置在输出文件夹(默认为out)中。这提供了一个更容易的部署场景,因为库和项目输出现在位于同一根文件夹下。
我们现在为 kotlin-js 编译器提供一个命令行选项,即no-stdlib。没有指定此选项,编译器使用捆绑的标准库。这是 M9 行为的改变。
我们现在可以直接在 Kotlin 代码中输出 JavaScript 代码
{% raw %}
{% endraw %}js("var value = document.getElementById('item')")
{% raw %}
{% endraw %}它将代码提供为参数,并将其直接注入到编译器生成的 AST JavaScript 代码中。 如您所见,我们在此版本中为 JavaScript 添加了大量新的改进,我们将在单独的文章中更详细地介绍这些改进。
现在,我们可以将属性标记为[platformStatic]
,以便它们的访问者从 Java 可视为静态方法。
任何对象的属性现在都会产生静态字段,以便它们可以轻松地从 Java 消费,即使不需要使用platformStatic注释进行装饰。
Kotlin 现在通过[native]
注释支持 JNI,在kotlin.jvm
包中定义(参见规范文档 这里 。)。要声明本机方法,只需将注释放在其上:
{% raw %}
{% endraw %}import kotlin.jvm.*
import kotlin.platform.*
class NativeExample {
native fun foo() // native method
class object {
platformStatic native fun bar() // static native method
}
}
{% raw %}
{% endraw %}这是一个 例 使用 Android 和 NDK 的原生声明。
IntelliJ IDEA 领域的一些改进包括:
我们已经增强了增量编译,并且使用 M10,它现在支持 Kotlin 和 Java 代码之间的依赖。现在,当您更改 Java 代码时,您的 Kotlin 代码的相关部分将被重新编译。作为提醒,增量编译在 Kotlin 编译器选项下激活。
现在,当我们在调试的时候重新编译 Kotlin 代码,它顺利地被重新加载到了难民程序中。
在调试会话期间,在评估表达式时,根据需要自动添加转换。例如,当从任何下载到特定类型时。
{% raw %}
{% endraw %}我们现在可以获得任何 Kotlin 符号的完整参考,就像我们一样 IntelliJ IDEA 在 Java 代码中 {% raw %}
{% endraw %}从类和包的使用创建
现在,使用创建可用于类和包,这对 TDD 工作流程有重大贡献。即使您不在做 TDD,它也会减少创建新元素的摩擦。
{% raw %}
{% endraw %}变更签名重构现在在基类功能被提升为使用泛型的情况下支持泛型。实质上,在下面的情况
{% raw %}
{% endraw %}open class Base<T> {
open fun baseMethod<K>(t: T, k: K) {}
}
class Derived<X>: Base<List<X>> {
override fun baseMethod<Y>(a: List<X>, b: Y) {}
}
{% raw %}
{% endraw %}如果Base.baseMethod的签名被更改为派生的签名,那么将其更改为baseMethod&lt; T&gt;(t:List&lt; T&gt;,k:K?)。碱性方法适当地更改为&gt; baseMethod&lt; Y&gt;(a:List&lt; Y&gt;,b:List&lt; X&gt;?
完成项目订购已得到改进,现在突出显示立即成员。智能完成现在可以找到预期类型的继承者。完成表现严重改善。
现在,您可以为 IDE 运行一个声明为[platformStatic]
主要功能的对象:
{% raw %}
{% endraw %}import kotlin.platform.*
object Hello {
platformStatic fun main(args: Array<String>) {
println("Hello")
}
}
{% raw %}
{% endraw %}只需右键单击该对象,然后选择运行...
如果您运行 Kotlin 代码与覆盖,编辑器现在标记覆盖和未覆盖的行(仅适用于 IntelliJ IDEA 14)。
IDE 插件现在可以自动配置 Maven 项目以使用 Kotlin / JS。另外,如果您有一个过时的 Kotlin 版本的运行时库,IDE 会要求您进行更新,现在您可以选择使用插件分发中的库,而不是将其复制到项目中。
要安装 M10,请更新 IntelliJ IDEA 14(或更早版本)中的插件,并且一如以往,您可以在我们的插件库中找到该插件。您也可以从中下载独立的编译器 发行页面 。