Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ so do familiarize yourself with the following guidelines.
PRs that add new API without a corresponding issue with positive feedback about the proposed implementation are
unlikely to be approved or reviewed.
* All new APIs must come with documentation and tests.
* All new APIs are initially released with the `@ExperimentalCoroutineApi` annotation and graduate later.
* All new APIs are initially released with the `@ExperimentalCoroutinesApi` annotation and graduate later.
* [Update the public API dumps](#updating-the-public-api-dump) and commit the resulting changes as well.
It will not pass the tests otherwise.
* If you plan large API additions, then please start by submitting an issue with the proposed API design
Expand Down
1 change: 1 addition & 0 deletions kotlinx-coroutines-core/api/kotlinx-coroutines-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -1012,6 +1012,7 @@ public final class kotlinx/coroutines/flow/FlowKt {
public static final fun asFlow (Lkotlin/ranges/IntRange;)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow (Lkotlin/ranges/LongRange;)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow (Lkotlin/sequences/Sequence;)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow (Lkotlinx/coroutines/flow/SharedFlow;)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow ([I)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow ([J)Lkotlinx/coroutines/flow/Flow;
public static final fun asFlow ([Ljava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
Expand Down
28 changes: 28 additions & 0 deletions kotlinx-coroutines-core/common/src/flow/operators/Share.kt
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,34 @@ private fun <T> CoroutineScope.launchSharingDeferred(
}
}

// -------------------------------- asFlow --------------------------------

/**
* Represents this shared flow and its subtypes as a plain flow,
* hiding its hot flow characteristics.
*
* Unlike simple upcasting, the returned flow prevents casting back to the original
* hot flow type, ensuring that implementation details remain encapsulated.
*
* Example:
* ```
* class Repository {
* private val _updates = MutableStateFlow("initial")
*
* // Exposes Flow interface without leaking MutableStateFlow implementation
* val updates: Flow<String> = _updates.asFlow()
* }
*
* // Usage
* val flow = repository.updates
* val mutableStateFlow = flow as? MutableStateFlow // null - cast prevented
* val stateFlow = flow as? StateFlow // null - cast prevented
* ```
*/
@ExperimentalCoroutinesApi
public fun <T> SharedFlow<T>.asFlow(): Flow<T> =
transform { value -> emit(value) }

// -------------------------------- asSharedFlow/asStateFlow --------------------------------

/**
Expand Down
34 changes: 34 additions & 0 deletions kotlinx-coroutines-core/common/test/flow/operators/AsFlowTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
@file:Suppress("PackageDirectoryMismatch")

package kotlinx.coroutines.flow

import kotlinx.coroutines.testing.*
import kotlin.test.*

class AsFlowTest : TestBase() {
@Test
fun testAsFlow() = runTest {
val mutableSharedFlow = MutableSharedFlow<Int>(replay = 3)
mutableSharedFlow.emit(value = 1)
mutableSharedFlow.emit(value = 2)
mutableSharedFlow.emit(value = 3)

val flow = mutableSharedFlow.asFlow()

assertEquals(
expected = listOf(1, 2, 3),
actual = flow
.take(count = 3)
.toList(),
)
}

@Test
fun testAsFlowIsNotSharedFlow() {
val mutableSharedFlow = MutableSharedFlow<Int>()

val flow = mutableSharedFlow.asFlow()

assertIsNot<SharedFlow<Int>>(flow)
}
}