作者为什么需要在Kotlin中的Flow中添加`>>`来捕获异常?

huangapple go评论59阅读模式
英文:

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&lt;Async&lt;List&lt;Task&gt;&gt;&gt; .

I don't know why the author need to add &lt;Async&lt;List&lt;Task&gt;&gt;&gt; for catch in Flow in Kotlin?

You know the .map{} needn't to add .map&lt;Async&lt;List&lt;Task&gt;&gt;&gt; {}

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&lt;Async&lt;List&lt;Task&gt;&gt;&gt; .

I don't know why the author need to add &lt;Async&lt;List&lt;Task&gt;&gt;&gt; for catch in Flow in Kotlin?

You know the the .map{} needn't to add .map&lt;Async&lt;List&lt;Task&gt;&gt;&gt; {}

private val _savedFilterType =
        savedStateHandle.getStateFlow(TASKS_FILTER_SAVED_STATE_KEY, ALL_TASKS)

    private val _filterUiInfo = _savedFilterType.map { getFilterUiInfo(it) }.distinctUntilChanged()
    private val _userMessage: MutableStateFlow&lt;Int?&gt; = MutableStateFlow(null)
    private val _isLoading = MutableStateFlow(false)
    private val _filteredTasksAsync =
        combine(taskRepository.getTasksStream(), _savedFilterType) { tasks, type -&gt;
            filterTasks(tasks, type)
        }
            .map { Async.Success(it) }   // .map &lt;Async&lt;List&lt;Task&gt;&gt;&gt; { Async.Success(it) }  isn&#39;t necessary
            .catch&lt;Async&lt;List&lt;Task&gt;&gt;&gt; { emit(Async.Error(R.string.loading_tasks_error)) }   //.catch { emit(Async.Error(R.string.loading_tasks_error)) }  will cause error


sealed class Async&lt;out T&gt; {
    object Loading : Async&lt;Nothing&gt;()    
    data class Error(val errorMessage: Int) : Async&lt;Nothing&gt;()    
    data class Success&lt;out T&gt;(val data: T) : Async&lt;T&gt;()
}

答案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&lt;Async.Success&lt;List&lt;Action&gt;&gt;&gt; 的类型不等于 Flow&lt;Async&lt;List&lt;Action&gt;&gt;&gt;,因此这里的唯一目的是明确地 "向下转型" 流。

英文:

catch on Flow is defined as: (https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/catch.html)

fun &lt;T&gt; Flow&lt;T&gt;.catch(action: suspend FlowCollector&lt;T&gt;.(cause: Throwable) -&gt; Unit): Flow&lt;T&gt;

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&lt;Async.Success&lt;List&lt;Action&gt;&gt;&gt;, which is not equal to Flow&lt;Async&lt;List&lt;Action&gt;&gt;&gt;, so the sole purpose here is to "downcast" the flow explicitely.

答案2

得分: 1

当您省略&lt;...&gt;时,您允许函数的通用类型被推断。对于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 &lt;…&gt;, 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&lt;Async&lt;List&lt;Task&gt;&gt;&gt; { 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&lt;Async&lt;_&gt;&gt; { Async.Success(it) }
.catch { emit(Async.Error(R.string.loading_tasks_error)) }

huangapple
  • 本文由 发表于 2023年6月5日 15:19:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/76404243.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定