英文:
Why do the author need to add <Async<List<Task>>> for catch in Flow in Kotlin?
问题
以下是翻译好的部分:
The following code comes from the project.
And _filteredTasksAsync
will return Flow<Async<List<Task>>>
.
I don't know why the author need to add <Async<List<Task>>>
for catch
in Flow in Kotlin?
You know the .map{}
needn't to add .map<Async<List<Task>>> {}
private val _savedFilterType = savedStateHandle.getStateFlow(TASKS_FILTER_SAVED_STATE_KEY, ALL_TASKS)
private val _filterUiInfo = _savedFilterType.map { getFilterUiInfo(it) }.distinctUntilChanged()
private val _userMessage: MutableStateFlow<Int?> = MutableStateFlow(null)
private val _isLoading = MutableStateFlow(false)
private val _filteredTasksAsync =
combine(taskRepository.getTasksStream(), _savedFilterType) { tasks, type -> filterTasks(tasks, type) }
.map { Async.Success(it) } // .map <Async<List<Task>>> { Async.Success(it) } isn't necessary
.catch<Async<List<Task>>> { emit(Async.Error(R.string.loading_tasks_error)) } //.catch { emit(Async.Error(R.string.loading_tasks_error)) } will cause error
sealed class Async<out T> {
object Loading : Async<Nothing>()
data class Error(val errorMessage: Int) : Async<Nothing>()
data class Success<out T>(val data: T) : Async<T>()
}
英文:
The following code comes from the project.
And _filteredTasksAsync
will return Flow<Async<List<Task>>>
.
I don't know why the author need to add <Async<List<Task>>>
for catch
in Flow in Kotlin?
You know the the .map{}
needn't to add .map<Async<List<Task>>> {}
private val _savedFilterType =
savedStateHandle.getStateFlow(TASKS_FILTER_SAVED_STATE_KEY, ALL_TASKS)
private val _filterUiInfo = _savedFilterType.map { getFilterUiInfo(it) }.distinctUntilChanged()
private val _userMessage: MutableStateFlow<Int?> = MutableStateFlow(null)
private val _isLoading = MutableStateFlow(false)
private val _filteredTasksAsync =
combine(taskRepository.getTasksStream(), _savedFilterType) { tasks, type ->
filterTasks(tasks, type)
}
.map { Async.Success(it) } // .map <Async<List<Task>>> { Async.Success(it) } isn't necessary
.catch<Async<List<Task>>> { emit(Async.Error(R.string.loading_tasks_error)) } //.catch { emit(Async.Error(R.string.loading_tasks_error)) } will cause error
sealed class Async<out T> {
object Loading : Async<Nothing>()
data class Error(val errorMessage: Int) : Async<Nothing>()
data class Success<out T>(val data: T) : Async<T>()
}
答案1
得分: 1
Flow
中的 catch
定义如下:(https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/catch.html)
fun <T> Flow<T>.catch(action: suspend FlowCollector<T>.(cause: Throwable) -> Unit): Flow<T>
因此,如果在 catch
中指定 <T>
,那只有在无法推断出类型时才需要,这不能仅从给定代码的上下文中确定,但我假设由于 map
中的 Action 类型可以被推断出,所以不需要显式的 <T>
,但这会使 Flow<Async.Success<List<Action>>>
的类型不等于 Flow<Async<List<Action>>>
,因此这里的唯一目的是明确地 "向下转型" 流。
英文:
catch
on Flow
is defined as: (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/catch.html)
fun <T> Flow<T>.catch(action: suspend FlowCollector<T>.(cause: Throwable) -> Unit): Flow<T>
Therefore, if you specify <T> in catch
, that would only be required if it could not be inferred, which cannot be determined solely from the context of the given code, but I am assuming that since the type of Action in map
could be inferred, the explicit <T> is not required, however it would make the type of the Flow<Async.Success<List<Action>>>
, which is not equal to Flow<Async<List<Action>>>
, so the sole purpose here is to "downcast" the flow explicitely.
答案2
得分: 1
当您省略<...>
时,您允许函数的通用类型被推断。对于map
,您可以始终省略它,因为类型可以推断为从 lambda 返回的类型。
在这里的catch
调用中,如果您发出与前面的map
操作返回的流的已知类型相同的类型,它将能够推断类型。然而,map
操作返回的流的返回类型是Async.Success
。在catch
操作符中,我们没有发出Async.Success
,因此类型不匹配,因此无法推断。我们需要指定最近的共同父类型,即Async
。
您还可以将通用类型规范移至map
行,然后在catch
行中就不需要了:
.map<Async<List<Task>>> { Async.Success(it) }
.catch { emit(Async.Error(R.string.loading_tasks_error)) }
据我所知,他们最近改进了 Kotlin 编译器,因此如果嵌套的通用类型不变,您不必指定每个细节,因此我认为这会起作用:
.map<Async<_>> { Async.Success(it) }
.catch { emit(Async.Error(R.string.loading_tasks_error)) }
英文:
When you omit <…>
, you are allowing the generic type of the function to be inferred. With map
, you can always omit it, because the type can be inferred to be whatever type you are returning from the lambda.
With the catch
call here, it would be able to infer the type if you were emitting the same type as the known type of the flow returned from the preceding map
operator. However, the returned type of the Flow from the map
operator is Async.Success
. In the catch
operator, we are not emitting Async.Success
so the types don’t match and therefore cannot be inferred. We need to specify the nearest common parent type which is just Async
.
You could also move the generic type specification up to the map
line, and then it wouldn’t be needed for the catch
line:
.map<Async<List<Task>>> { Async.Success(it) }
.catch { emit(Async.Error(R.string.loading_tasks_error)) }
IIRC, they have recently improved the Kotlin compiler so you don’t have to specify every detail of a nested generic type if it’s not changing, so I think this would work:
.map<Async<_>> { Async.Success(it) }
.catch { emit(Async.Error(R.string.loading_tasks_error)) }
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论