Передача различных типов generi c для работы - PullRequest
0 голосов
/ 26 мая 2020

У меня есть функция в Kotlin, которая может принимать общий объект c в качестве параметра. Эти два объекта не связаны между собой и не имеют общих базовых типов. Однако они оба реализуют одни и те же функции. Я хотел бы повторно использовать эти функции в своей функции. Что-то вроде этих строк:

fun storeUser(datastore: Any) {
   datastore.storeName("John")
}

// Call the function
val datastore1 = DataStore1()
storeUser(datastore1)

val datastore2 = DataStore2()
storeUser(datastore2)

И DataStore1, и DataStore2 имеют функцию, называемую storeName. Есть ли способ в Kotlin повторно использовать эту функцию в функции storeUser? Я попытался поиграть с Generics, но это оказалось невозможным.

Пример кода выше прост. В моем реальном приложении, помимо storeName, есть еще много функций. Если у меня не может быть общей функции для хранения моих данных, мне нужно будет создать две отдельные функции и продублировать хранилище для обеих. Это отстой.

Ответы [ 2 ]

3 голосов
/ 26 мая 2020

Я рекомендую использовать общий интерфейс для обоих классов. Если они предоставляются сторонней библиотекой, вы можете обернуть их в свои собственные классы и интерфейс.

Если вы не хотите этого делать, вы можете просто проверить тип параметра в storeUser function:

 fun storeUser(datastore: Any) {
   when(datastore) {
     is DataStore1 -> datastore.storeName("John")
     is DataStore2 -> datastore.storeName("John")
     else -> throw IllegalArgumentException()
   }
 }

Но учтите, что если в будущем у вас будет другое хранилище данных, вам нужно будет добавить еще одно предложение is к этой функции. Это делает этот код не очень удобным в обслуживании ...

Лучшее решение

Если вы создаете интерфейс Datastore:

interface Datastore {
  fun storeName(name: String)
} 

и сделайте так, чтобы ваши хранилища данных реализовали это:

class Datastore1 : Datastore {
  //Datastore1.storeName implementation
}

class Datastore2 : Datastore {
  //Datastore2.storeName implementation
}

Тогда вам не нужно проверять типы в функции storeUser. Просто измените его тип параметра на Datastore:

fun storeUser(datastore: Datastore) {
  datastore.storeName("John")
}

Если Datastore1 и Datastore2 предоставлены сторонней библиотекой , вы можете обернуть их в свои собственные классы и реализовать свои Datastore интерфейс:

class FirstDatastore : Datastore {

  private val datastore = DataStore1()

  override fun storeName(name: String) {
    datastore.storeName(name)
  }

}

class SecondDatastore : Datastore {

  private val datastore = DataStore2()

  override fun storeName(name: String) {
    datastore.storeName(name)
  }

}

Итак, вы можете вызывать свою функцию, используя свои классы:

val datastore1 = FirstDatastore()
storeUser(datastore1)

val datastore2 = SecondDatastore()
storeUser(datastore2)
0 голосов
/ 26 мая 2020

Как я сказал в комментарии к вопросу, действительно было бы лучше написать общий интерфейс для этих классов. Если это невозможно, потому что классы происходят от внешней зависимости, второе, что лучше всего сделать, - это обернуть код, как это сделал Гектор.

Kotlin - язык со статической типизацией, поэтому, к сожалению, такой код приводит к большому дублированию. Если вы не хотите писать новую оболочку для каждого нового экземпляра DataStore, вы можете использовать отражение для динамического вызова. Таким образом, вам нужно написать определение только один раз. Однако вы используете go все преимущества проверок stati c во время компиляции, так что это не совсем хорошая идея. Хотя это было хорошо сделать в качестве упражнения. ?

class WrappedDataStore<T : Any>(private val dataStore: T) {
    private fun callDynamically(methodName: String, vararg args: Any?) {
        val argTypes = args.map { it?.let { it::class.java} }.toTypedArray()
        dataStore.javaClass
            .getMethod(methodName, *argTypes)
            .invoke(dataStore, *args)
    }

    fun storeName(name: String) = callDynamically("storeName", name)
}

fun <T : Any> storeUser(dataStore: WrappedDataStore<T>) =
    dataStore.storeName("John")

fun main() {
    val one = WrappedDataStore(DataStore1())
    val two = WrappedDataStore(DataStore2())

    one.storeName("foo")
    two.storeName("bar")

    storeUser(one)
    storeUser(two)
}

class DataStore1 {
    fun storeName(foo: String) = println("DataStore1 $foo")
}

class DataStore2 {
    fun storeName(bar: String) = println("DataStore2 $bar")
}

Вывод:

DataStore1 foo
DataStore2 bar
DataStore1 John
DataStore2 John
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...