不同的kotlin-stdlibs的解释

评论 2 浏览 0 2019-09-15

(不确定你是否应该使用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 commitrelease 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 (*)

我们还可以在mavenCentralhttps://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才完成,

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从我的项目中删除之前,请让我知道!

最后更新2022-11-13
2 个评论
#1 Davis Miyashiro 2021-05-29

很好的帖子,但你应该在结论上加一个说明,自Kotlin 1.4以来,你不再需要在gradle上声明stdlib。https://kotlinlang.org/docs/whatsnew14.html#dependency-on-the-standard-library-added-by-default

Martin Bonnin 2022-06-30

谢谢你,对不起,我刚刚更新了帖子,耽误了时间。

#2 Anderson Lameck 2019-12-31

非常感谢你的启迪。

Martin Bonnin 2020-01-02

谢谢你,很高兴你喜欢它,并祝你新年快乐!!。

标签