Kotlin 伴生对象 - Init 块 - Typealias
Kotlin Companion Object - Init Block - Typealias
我正在尝试解决一个 Kotlin 问题,它有一个混合的 OOP 结构,所以我无法解决它,抱歉,如果这个问题是基本的。我来自 Java,所以我是 Kotlin 的新手。
我有 DataSource.kt
,它有 DataSource
、Person
、FetchResponse
和 FetchError
class。我不明白的是 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".
在我的 Main class 中,我只是想获取 people
的列表以及他们的 id
,我是这样尝试的;
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)
但它表明那是错误的,所以我一开始就卡住了。我该如何解决这个问题?
DataSource 是一个 class
,而不是一个 object
,因此您必须先实例化它才能使用它。它具有伴随对象这一事实与您无关,因为伴随对象没有任何 public 属性或函数。
FetchCompletionHandler
是一个回调函数。 typealias
只是让代码更容易描述,而不必在每次需要提及类型时都编写 (FetchResponse?, FetchError?) -> 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)
我正在尝试解决一个 Kotlin 问题,它有一个混合的 OOP 结构,所以我无法解决它,抱歉,如果这个问题是基本的。我来自 Java,所以我是 Kotlin 的新手。
我有 DataSource.kt
,它有 DataSource
、Person
、FetchResponse
和 FetchError
class。我不明白的是 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".
在我的 Main class 中,我只是想获取 people
的列表以及他们的 id
,我是这样尝试的;
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)
但它表明那是错误的,所以我一开始就卡住了。我该如何解决这个问题?
DataSource 是一个 class
,而不是一个 object
,因此您必须先实例化它才能使用它。它具有伴随对象这一事实与您无关,因为伴随对象没有任何 public 属性或函数。
FetchCompletionHandler
是一个回调函数。 typealias
只是让代码更容易描述,而不必在每次需要提及类型时都编写 (FetchResponse?, FetchError?) -> 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)