不同的kotlin-stdlibs的解释
(不确定你是否应该使用kotlin-stdlib-jdk8......)
编辑2022-06-30:重写了结论,以强调Kotlin Gradle插件现在会自动添加stdlib。
我喜欢Kotlin,因为它是一种简明而强大的语言,具有非常合理的默认值和设计决定。在这方面,它采用了Python的禅意。
应该有一个--最好是只有一个--明显的方法来做。
但有一个地方失败了,那就是stdlib的配置。这有点讽刺,因为它也是你作为一个Kotlin新手首先要配置的东西之一。
下面这几项中,什么是最好的?对于部分支持Java 8的安卓设备,您应该选择什么?
// so many stdlib to choose from!
implementation("org.jetbrains.kotlin:kotlin-stdlib-jre8")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jre7")
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")
kotlin-stdlib-jre7和kotlin-stdlib-jre8
这些是简单的。它们在两年前就被废弃了,取而代之的是-jdk7和-jdk8,所以你可以忘记它们。这样做是为了适应java9的模块系统,更确切地说,是为了避免拆分包。你可以从github commit和release notes中阅读细节。
你可能偶尔会看到它们。不要使用它们。
kotlin-stdlib,-jdk7和-jdk8
根据Kotlin doc的说法。
The Kotlin standard library kotlin-stdlib targets Java 6 and above. There are extended versions of the standard library that add support for some of the features of JDK 7 and JDK 8. To use these versions, add one of the following dependencies instead of kotlin-stdlib.
事实上,如果你看一下kotlin-stdlib-jdk7 pom文件,你可以看到它过渡性地依赖于kotlin-stdlib。
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>1.3.50</version>
<scope>compile</scope>
</dependency>
如果你包含了kotlin-stdlib-jdk7,它就会拉动kotlin-stdlib。
如果你包含kotlin-stdlib-jdk8,它将拉动kotlin-stdlib-jdk7和kotlin-stdlib。你也可以通过运行./gradlew :dependencies
来检查。
compileClasspath - Compile classpath for compilation 'main' (target (jvm)).
\--- org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50
+--- org.jetbrains.kotlin:kotlin-stdlib:1.3.50
| +--- org.jetbrains.kotlin:kotlin-stdlib-common:1.3.50
| \--- org.jetbrains:annotations:13.0
\--- org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.50
\--- org.jetbrains.kotlin:kotlin-stdlib:1.3.50 (*)
我们还可以在mavenCentral(https://repo.maven.apache.org/maven2/org/jetbrains/kotlin/)上查看大小。
martin@bowser-castle$ ls -al kotlin-stdlib-*.jar
1326269 Sep 14 16:58 kotlin-stdlib-1.3.50.jar
3129 Sep 14 16:58 kotlin-stdlib-jdk7-1.3.50.jar
15476 Sep 14 16:58 kotlin-stdlib-jdk8-1.3.50.jar
kotlin-stdlib是1MB以上,而其他两个最多只有几KB。这证实了大部分的功能都在kotlin-stdlib中,并在-jdk7和-jdk8中增加了一些功能。
所以让我们拉出kotlin-stdlib-jdk8,我们就能得到一切,对吗?嗯......不那么肯定,让我们看看这些工件里面有什么。
kotlin-stdlib
含有。
- 大部分的功能。集合、范围、数学、Regex、文件扩展、锁,等等。你日常使用的大部分功能都在kotlin-stdlib中。
kotlin-stdlib-jdk7
含有:
- Reflection-free suppressed异常
Suppressed的异常是在Java 7中与try-with-resources同时添加的。它在释放资源时抛出的异常中提供了更多信息。
val closeable = object: Closeable {
override fun close() {
throw Exception("exception from close")
}
}
closeable.use {
throw Exception("exception from use")
}
// Java6:
// Exception in thread "main" java.lang.Exception: exception from use
// at com.example.MainKt.main(Main.kt:13)
// Java7 +:
//
// Exception in thread "main" java.lang.Exception: exception from use
// at MainKt.main(Main.kt:11)
// at MainKt.main(Main.kt)
// Suppressed: java.lang.Exception: exception from close
// at MainKt$main$closeable$1.close(Main.kt:6)
// at kotlin.io.CloseableKt.closeFinally(Closeable.kt:56)
// at MainKt.main(Main.kt:10)
// ... 1 more
kotlin-stdlib在Java 7+上支持这个带反射的。
kotlin-stdlib-jd7也做了同样的事情,没有反射。
除了Closeable类型外,Java 7还引入了AutoCloseable。kotlin-stdlib-jdk7在该类型上也添加了use
扩展函数。
kotlin-stdlib-jdk8
含有。
- Java 8的流扩展
kotlin-stdlib-jdk8增加了扩展函数,以将java.util.Stream转换为kotlin.sequences.Sequence和kotlin.collection.List(source)。
- Duration 扩展
kotlin-stdlib-jdk8增加了扩展函数,以便在java.time.Duration和kotlin.time.Duration之间进行转换(source)。
- 正则表达式中的命名组
kotlin-stdlib-jdk8增加了对命名组的支持。(?<name>group)
将捕获反向引用“name”下的组的匹配。
val matchResult = Regex("(?<key>.*)=(?<value>.*)").matchEntire("jdk=8")
if (matchResult != null) {
println("key=${matchResult.groups.get("key")!!.value}")
}
请注意,虽然命名组在Java 7上开始实施,但直到Java 8才完成,。
- 支持ThreadLocalRandom的功能。
kotlin-stdlib-jdk8将默认使用ThreadLocalRandom来Random.Default。这应该可以消除多线程情况下的一些争论(stackoverflow),尽管在Java 6&7上有一个后备方案,使用ThreadLocal来模拟ThreadLocalRandom。同样,ThreadLocalRandom也是在Java 7上开始的,但是错误的,所以它只在Java 8上被添加。
在安卓系统上使用什么?
每次,-jdk7和-jdk8工件都会增加PlatformImplementations和扩展函数的组合。但在设备上真正使用的是什么?
我的pixel 3报告System.getProperty("java.specification.version")="0.9"
。0.9低于Java 7。我甚至不确定它是否符合Java 6。此外,System.getProperty("java.vm.name")
返回"Dalvik"
。因此,看起来这是在报告一些定制的Android虚拟机。
这在运行时不会被stdlib所接受。这意味着无论你在classpath中放了什么工件,stdlib都会退回到默认的PlatformImplementations。
// https://github.com/JetBrains/kotlin/blob/65244b4bea81f737466618927d4f3afe339cad0d/libraries/stdlib/jvm/src/kotlin/internal/PlatformImplementations.kt#L42
internal val IMPLEMENTATIONS: PlatformImplementations = run {
val version = getJavaVersion()
if (version >= 0x10008) {
try {
return@run castToBaseType<PlatformImplementations>(Class.forName("kotlin.internal.jdk8.JDK8PlatformImplementations").newInstance())
} catch (e: ClassNotFoundException) { }
try {
return@run castToBaseType<PlatformImplementations>(Class.forName("kotlin.internal.JRE8PlatformImplementations").newInstance())
} catch (e: ClassNotFoundException) { }
}
if (version >= 0x10007) {
try {
return@run castToBaseType<PlatformImplementations>(Class.forName("kotlin.internal.jdk7.JDK7PlatformImplementations").newInstance())
} catch (e: ClassNotFoundException) { }
try {
return@run castToBaseType<PlatformImplementations>(Class.forName("kotlin.internal.JRE7PlatformImplementations").newInstance())
} catch (e: ClassNotFoundException) { }
}
PlatformImplementations()
}
由于Android支持API级别为26+的java.time.Duration和API级别为24+的Stream API(doc),这就意味着。
- jdk8的ThreadLocalRandom不会在任何API层面上使用。
- jdk8 命名的组将在所有API级别上的运行时抛出。
- jdk8 的Duration 扩展将在API级别26以上的情况下工作。
- jdk8的Stream扩展将在API级别24以上的情况下工作。
- jdk7的reflection-free suppressed异常不会在任何API层面上被使用。
持续时间扩展实际上就是两行实验性代码。流扩展要多一些,但如果你使用Kotlin,你可以(当然也应该)用序列或集合来代替它们。
另一方面,依赖kotlin-stdlib-jdk8会增加JDK7/JDK8PlatformImplementations代码。虽然它不是很大,但R8将无法剥离它,因为它在运行时的使用取决于Java规范的版本,而这个版本在Android上似乎一直是0.9。
总结
综合考虑,普通的kotlin-stdlib似乎是Android上的最佳候选者。由于-jdk7和-jdk8中的大部分功能都无法访问,它避免了下载额外的工件和加载额外的字节码。这似乎有点违反直觉,因为我本以为会是相反的情况。
请注意,如果您使用 Kotlin 1.4+,Kotlin Gradle 插件现在默认添加 -jdk8 stdlib。如果你想选择退出,你可以在gradle.properties
中用kotlin.stdlib.default.dependency=false
选择退出。
你用的是什么?在我把所有的jdk8从我的项目中删除之前,请让我知道!
谢谢你,对不起,我刚刚更新了帖子,耽误了时间。
非常感谢你的启迪。
谢谢你,很高兴你喜欢它,并祝你新年快乐!!。
很好的帖子,但你应该在结论上加一个说明,自Kotlin 1.4以来,你不再需要在gradle上声明stdlib。https://kotlinlang.org/docs/whatsnew14.html#dependency-on-the-standard-library-added-by-default