From 9fe6e97cb814563112a3f82c123992313c7a7ef1 Mon Sep 17 00:00:00 2001 From: Kevin Bierhoff Date: Wed, 13 Apr 2022 04:34:06 -0700 Subject: [PATCH] Extensions for java.util.Optional (KT-50484) Co-authored-by: Ilya Gorbunov --- libraries/stdlib/jdk8/java9/module-info.java | 1 + .../src/kotlin/jvm/optionals/Optionals.kt | 69 +++++++++++++++ .../jdk8/test/jvm/optionals/OptionalsTest.kt | 87 +++++++++++++++++++ libraries/stdlib/src/Module.md | 4 + .../kotlin-stdlib-jdk8.txt | 10 +++ 5 files changed, 171 insertions(+) create mode 100644 libraries/stdlib/jdk8/src/kotlin/jvm/optionals/Optionals.kt create mode 100644 libraries/stdlib/jdk8/test/jvm/optionals/OptionalsTest.kt diff --git a/libraries/stdlib/jdk8/java9/module-info.java b/libraries/stdlib/jdk8/java9/module-info.java index ea312e90802..dedc66a8f20 100644 --- a/libraries/stdlib/jdk8/java9/module-info.java +++ b/libraries/stdlib/jdk8/java9/module-info.java @@ -3,6 +3,7 @@ module kotlin.stdlib.jdk8 { requires transitive kotlin.stdlib; requires kotlin.stdlib.jdk7; + exports kotlin.jvm.optionals; exports kotlin.jvm.jdk8; exports kotlin.collections.jdk8; exports kotlin.streams.jdk8; diff --git a/libraries/stdlib/jdk8/src/kotlin/jvm/optionals/Optionals.kt b/libraries/stdlib/jdk8/src/kotlin/jvm/optionals/Optionals.kt new file mode 100644 index 00000000000..538870eaf91 --- /dev/null +++ b/libraries/stdlib/jdk8/src/kotlin/jvm/optionals/Optionals.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.jvm.optionals + +import java.util.Optional + +/** + * Returns this [Optional]'s value if [present][Optional.isPresent], or otherwise `null`. + */ +@SinceKotlin("1.7") +@ExperimentalStdlibApi +public fun Optional.getOrNull(): T? = orElse(null) + +/** + * Returns this [Optional]'s value if [present][Optional.isPresent], or otherwise [defaultValue]. + */ +@SinceKotlin("1.7") +@ExperimentalStdlibApi +public fun Optional.getOrDefault(defaultValue: R): R = if (isPresent) get() else defaultValue + +/** + * Returns this [Optional]'s value if [present][Optional.isPresent], or otherwise the result of the [defaultValue] function. + */ +@SinceKotlin("1.7") +@ExperimentalStdlibApi +public inline fun Optional.getOrElse(defaultValue: () -> R): R = + if (isPresent) get() else defaultValue() + +/** + * Appends this [Optional]'s value to the given [destination] collection if [present][Optional.isPresent]. + */ +@SinceKotlin("1.7") +@ExperimentalStdlibApi +public fun > Optional.toCollection(destination: C): C { + if (isPresent) { + destination.add(get()) + } + return destination +} + +/** + * Returns a new read-only list of this [Optional]'s value if [present][Optional.isPresent], or otherwise an empty list. + * The returned list is serializable (JVM). + */ +@SinceKotlin("1.7") +@ExperimentalStdlibApi +public fun Optional.toList(): List = + if (isPresent) listOf(get()) else emptyList() + +/** + * Returns a new read-only set of this [Optional]'s value if [present][Optional.isPresent], or otherwise an empty set. + * The returned set is serializable (JVM). + */ +@SinceKotlin("1.7") +@ExperimentalStdlibApi +public fun Optional.toSet(): Set = + if (isPresent) setOf(get()) else emptySet() + +/** + * Returns a new sequence for this [Optional]'s value if [present][Optional.isPresent], or otherwise an empty sequence. + * The returned set is serializable (JVM). + */ +@SinceKotlin("1.7") +@ExperimentalStdlibApi +public fun Optional.asSequence(): Sequence = + if (isPresent) sequenceOf(get()) else emptySequence() diff --git a/libraries/stdlib/jdk8/test/jvm/optionals/OptionalsTest.kt b/libraries/stdlib/jdk8/test/jvm/optionals/OptionalsTest.kt new file mode 100644 index 00000000000..822c822e495 --- /dev/null +++ b/libraries/stdlib/jdk8/test/jvm/optionals/OptionalsTest.kt @@ -0,0 +1,87 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package kotlin.jdk8.jvm.optionals.test + +import java.util.Optional +import kotlin.test.* +import kotlin.jvm.optionals.* + +class OptionalsTest { + @Test + fun getOrNull() { + assertEquals("foo", Optional.of("foo").getOrNull()) + assertNull(Optional.empty().getOrNull()) + } + + @Test + fun getOrDefault() { + assertEquals("foo", Optional.of("foo").getOrDefault("bar")) + assertEquals("bar", Optional.empty().getOrDefault("bar")) + + // Return type can be a supertype + assertNull(Optional.empty().getOrDefault(null)) + assertEquals(5.0, Optional.empty().getOrDefault(5.0)) + } + + @Test + fun getOrElse() { + assertEquals("foo", Optional.of("foo").getOrElse { throw AssertionError() }) + assertEquals("bar", Optional.empty().getOrElse { "bar" }) + + // Return type can be a supertype + assertNull(Optional.empty().getOrElse { null }) + assertEquals(5.0, Optional.empty().getOrElse { 5.0 }) + } + + @Test + fun getOrElse_propagatesException() { + val e = assertFailsWith { + Optional.empty().getOrElse { throw IllegalStateException("ran") } + } + assertEquals("ran", e.message) + } + + @Test + fun optionalToCollection_presentAddsValue() { + val dest = mutableListOf() + Optional.of("foo").toCollection(dest) + assertEquals(listOf("foo"), dest) + } + + @Test + fun optionalToCollection_emptyAddsNothing() { + val dest = mutableListOf() + Optional.empty().toCollection(dest) + assertEquals(emptyList(), dest) + } + + @Test + fun optionalToList() { + assertEquals(listOf("foo"), Optional.of("foo").toList()) + assertEquals(emptyList(), Optional.empty().toList()) + + // List element type can be a supertype + assertEquals(listOf("foo"), Optional.of("foo").toList()) + } + + @Test + fun optionalToSet() { + assertEquals(setOf("foo"), Optional.of("foo").toSet()) + assertEquals(emptySet(), Optional.empty().toSet()) + + // List element type can be a supertype + assertEquals(setOf("foo"), Optional.of("foo").toSet()) + } + + @Test + fun optionalAsSequence() { + assertEquals(listOf("foo"), Optional.of("foo").asSequence().toList()) + assertEquals(emptyList(), Optional.empty().asSequence().toList()) + + // List element type can be a supertype + assertEquals(listOf("foo"), Optional.of("foo").asSequence().toList()) + } +} diff --git a/libraries/stdlib/src/Module.md b/libraries/stdlib/src/Module.md index 259e7a2ef44..b80c129b2e6 100644 --- a/libraries/stdlib/src/Module.md +++ b/libraries/stdlib/src/Module.md @@ -71,6 +71,10 @@ Functions and other APIs specific to the JavaScript platform. Functions and annotations specific to the Java platform. +# Package kotlin.jvm.optionals + +Convenience extension functions for `java.util.Optional` to simplify Kotlin-Java interop. + # Package kotlin.math Mathematical functions and constants. diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-jdk8.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-jdk8.txt index a0ae21027fc..0933ac146cb 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-jdk8.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-jdk8.txt @@ -1,3 +1,13 @@ +public final class kotlin/jvm/optionals/OptionalsKt { + public static final fun asSequence (Ljava/util/Optional;)Lkotlin/sequences/Sequence; + public static final fun getOrDefault (Ljava/util/Optional;Ljava/lang/Object;)Ljava/lang/Object; + public static final fun getOrElse (Ljava/util/Optional;Lkotlin/jvm/functions/Function0;)Ljava/lang/Object; + public static final fun getOrNull (Ljava/util/Optional;)Ljava/lang/Object; + public static final fun toCollection (Ljava/util/Optional;Ljava/util/Collection;)Ljava/util/Collection; + public static final fun toList (Ljava/util/Optional;)Ljava/util/List; + public static final fun toSet (Ljava/util/Optional;)Ljava/util/Set; +} + public final class kotlin/streams/jdk8/StreamsKt { public static final fun asSequence (Ljava/util/stream/DoubleStream;)Lkotlin/sequences/Sequence; public static final fun asSequence (Ljava/util/stream/IntStream;)Lkotlin/sequences/Sequence;