【问题标题】:Kotlin Companion Object - Init Block - TypealiasKotlin 伴随对象 - 初始化块 - 类型别名
【发布时间】:2021-11-23 08:31:25
【问题描述】:

我正在尝试解决 Kotlin 问题,它具有混合的 OOP 结构,所以我无法解决,如果这个问题是基本问题,抱歉。我来自 Java,所以我是 Kotlin 的新手。

我有 DataSource.kt,它有 DataSourcePersonFetchResponseFetchError 类本身。我不明白的是FetchCompletionHandler typealias

import android.os.Handler
import android.os.Looper
import kotlin.collections.ArrayList
import kotlin.math.min
import kotlin.random.Random


data class Person(val id: Int, val fullName: String)

data class FetchResponse(val people: List<Person>, val next: String?)

data class FetchError(val errorDescription: String)

typealias FetchCompletionHandler = (FetchResponse?, FetchError?) -> Unit

private data class ProcessResult(val fetchResponse: FetchResponse?, val fetchError: FetchError?, val waitTime: Double)

class DataSource {
    companion object {
        private var people: List<Person> = listOf()
    }

    private object Constants {
        val peopleCountRange: ClosedRange<Int> = 100..200 // lower bound must be > 0, upper bound must be > lower bound
        val fetchCountRange: ClosedRange<Int> = 5..20 // lower bound must be > 0, upper bound must be > lower bound
        val lowWaitTimeRange: ClosedRange<Double> = 0.0..0.3 // lower bound must be >= 0.0, upper bound must be > lower bound
        val highWaitTimeRange: ClosedRange<Double> = 1.0..2.0 // lower bound must be >= 0.0, upper bound must be > lower bound
        const val errorProbability = 0.05 // must be > 0.0
        const val backendBugTriggerProbability = 0.05 // must be > 0.0
        const val emptyFirstResultsProbability = 0.1 // must be > 0.0
    }

    init {
        initializeData()
    }

    public fun fetch(next: String?, completionHandler: FetchCompletionHandler) {
        val processResult = processRequest(next)

        Handler(Looper.getMainLooper()).postDelayed({
            completionHandler(processResult.fetchResponse, processResult.fetchError)
        },(processResult.waitTime * 1000).toLong())
    }

    private fun initializeData() {
        if (people.isNotEmpty()) {
            return
        }
        val newPeople: ArrayList<Person> = arrayListOf()
        val peopleCount: Int = RandomUtils.generateRandomInt(range = Constants.peopleCountRange)
        for (index in 0 until peopleCount) {
            val person = Person(id = index + 1, fullName = PeopleGen.generateRandomFullName())
            newPeople.add(person)
        }
        people = newPeople.shuffled()
    }

    private fun processRequest(next: String?): ProcessResult {
        var error: FetchError? = null
        var response: FetchResponse? = null
        val isError = RandomUtils.roll(probability = Constants.errorProbability)
        val waitTime: Double
        if (isError) {
            waitTime = RandomUtils.generateRandomDouble(range = Constants.lowWaitTimeRange)
            error = FetchError(errorDescription = "Internal Server Error")
        } else {
            waitTime = RandomUtils.generateRandomDouble(range = Constants.highWaitTimeRange)
            val fetchCount = RandomUtils.generateRandomInt(range = Constants.fetchCountRange)
            val peopleCount = people.size
            val nextIntValue = try {
                next!!.toInt()
            } catch (ex: Exception) {
                null
            }
            if (next != null && (nextIntValue == null || nextIntValue < 0)) {
                error = FetchError(errorDescription = "Parameter error")
            } else {
                val endIndex: Int = min(peopleCount, fetchCount + (nextIntValue ?: 0))
                val beginIndex: Int = if (next == null) 0 else min(nextIntValue!!, endIndex)
                var responseNext: String? = if (endIndex >= peopleCount) null else endIndex.toString()
                var fetchedPeople: ArrayList<Person> = ArrayList(people.subList(beginIndex, endIndex)) // begin ile end ayni olunca bos donuyor mu?
                if (beginIndex > 0 && RandomUtils.roll(probability = Constants.backendBugTriggerProbability)) {
                    fetchedPeople.add(0, people[beginIndex - 1])
                } else if (beginIndex == 0 && RandomUtils.roll(probability = Constants.emptyFirstResultsProbability)) {
                    fetchedPeople = arrayListOf()
                    responseNext = null
                }
                response = FetchResponse(people = fetchedPeople, next = responseNext)
            }
        }
        return ProcessResult(response, error, waitTime)
    }
}

// Utils
private object RandomUtils {

    fun generateRandomInt(range: ClosedRange<Int>): Int = Random.nextInt(range.start, range.endInclusive)

    fun generateRandomDouble(range: ClosedRange<Double>): Double = Random.nextDouble(range.start, range.endInclusive)

    fun roll(probability: Double): Boolean {
        val random = Random.nextDouble(0.0, 1.0)
        return random <= probability
    }
}

private object PeopleGen {
    private val firstNames = listOf("Fatma", "Mehmet", "Ayşe", "Mustafa", "Emine", "Ahmet", "Hatice", "Ali", "Zeynep", "Hüseyin", "Elif", "Hasan", "İbrahim", "Can", "Murat", "Özlem")
    private val lastNames = listOf("Yılmaz", "Şahin", "Demir", "Çelik", "Şahin", "Öztürk", "Kılıç", "Arslan", "Taş", "Aksoy", "Barış", "Dalkıran")

    fun generateRandomFullName(): String {
        val firstNamesCount = firstNames.size
        val lastNamesCount = lastNames.size
        val firstName = firstNames[RandomUtils.generateRandomInt(range = 0 until firstNamesCount)]
        val lastName = lastNames[RandomUtils.generateRandomInt(range = 0 until lastNamesCount)]
        return "${firstName} ${lastName}"
    }
}

在文档中,我看到了这些规则;

1) In order to fetch first set of people, use `DataSource`s `fetch` method by passing null into its `next` parameter and a completion handler.
2) "Completion Handler" has 2 parameters in its signature. A response or an error instance is passed when the operation finishes. They can't be both null or hold non-null references at a time.
3) When success, `FetchResponse` instance is passed into completion handler. This instance has people array and a `next` identifier which can be used for pagination.
4) Pass successful response's `next` identifier into `fetch` method in order to get next "pages".

在我的主要课程中,我只是想用他们的ids 获取people 的列表,我尝试过这样;

import DataSource
import FetchCompletionHandler
import FetchResponse
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val people = DataSource
        val fetchResponse: FetchResponse(people,null)
    }
}

或喜欢;

        var fetchCompletionHandler:FetchCompletionHandler? = null
        var myHandler = fetchCompletionHandler?.let { DataSource().fetch(null, it) }
        var mySource = DataSource
        var x = FetchResponse(mySource,null)

但它表明那是错误的,所以我一开始就被卡住了。我该如何解决这个问题?

【问题讨论】:

  • 我不明白问题出在哪里。你有错误吗?有什么不符合您的预期吗? FetchCompletionHandler 只是(FetchResponse?, FetchError?) -&gt; Unit 类型的名称,这是一个以FetchResponse?FetchError? 为参数并返回Unit 的函数的类型。
  • @marstran 问题是,我无法在我的主课中获取人员列表。我还没有写它的语法,所以我需要帮助
  • @Tenfour04 如何写来获取主类中的列表?

标签: android kotlin oop type-alias


【解决方案1】:

DataSource 是class,而不是object,因此您必须先实例化它才能使用它。它具有伴生对象这一事实与您无关,因为伴生对象没有任何公共属性或函数。

FetchCompletionHandler 是一个回调函数。 typealias 只是更容易在代码中描述,而无需在每次需要提及类型时都编写 (FetchResponse?, FetchError?) -&gt; Unit

由于回调是 fetch 函数的最后一个参数,您可以使用尾随 lambda 语法调用它。在这种情况下,lambda 是您的 FetchCompletionHandler。 lambda 的两个参数是FetchResponse?FetchError?,在typealias 中定义。

所以,你可以这样使用它:

class MainActivity : AppCompatActivity() {

    private val dataSource = DataSource()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        dataSource.fetch(null) { response, error ->
            // do something with the FetchResponse? and FetchError? here when they arrive
            when {
                response != null -> { }
                error!= null -> { }
            }
        }
    }
}

您实际上应该将 DataSource 放在 ViewModel 中,这样就不必在每次屏幕旋转时都重新创建它。由于它支持分页,因此您不想在屏幕旋转时失去位置。

您可以将函数分配给属性,而不是尾随 lambda 语法:

val callback: FetchCompletionHandler = { response, error ->
    //...
}

// ...
dataSource.fetch(null, callback)

或者您可以编写一个成员函数并使用函数引用语法:: 传递它。

private fun handleFetch(response: FetchResponse?, error: FetchError?) {
    //...
}

// ...
dataSource.fetch(null, ::handleFetch)

【讨论】:

  • 感谢您的详细回答,这很有帮助,但我仍然不知道如何创建字符串元素列表,例如["${firstName1} ${lastName1}","${firstName2} ${lastName2}",...]
  • 我正是需要这个,因为我会在 RecyclerView 中使用这个生成的列表
  • 这与您发布的问题完全不同。
猜你喜欢
  • 2017-03-13
  • 1970-01-01
  • 2011-04-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-23
  • 2017-10-04
  • 1970-01-01
相关资源
最近更新 更多