Параметры функции высшего порядка Kotlin: передача подтипов - PullRequest
4 голосов
/ 08 апреля 2019

Я столкнулся с проблемой с параметрами функции в Kotlin.Я объясню проблему с помощью некоторого кода.

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

open class A (val i: Int)
class B (val j: Int) : A(j)

fun f(x: A){
    print(x)
}

fun test_f(){
    f(A(1))
    f(B(1)) //no problem
}

Я пытался имитировать это с помощью параметров функции.

fun g(x: (A)->Int){
    print(x)
}

fun test_g(){
    val l1 = { a: A -> a.hashCode()}
    g(l1)

    val l2 = { b: B -> b.hashCode()}
    g(l2) //Error: Type mismatch. Required: (A)->Int, Found: (B)->Int
}

Кажется, что тип функции(B) -> Int не является подтипом (A) -> Int. Каков наилучший способ решения этой проблемы?

Моя первоначальная проблема состоит в определении функции более высокого порядка в A.h, которая принимает функцию z: (A) -> X в качестве параметра,И я хочу вызвать h для объекта типа B и передать функцию z: (B) -> X.

Обновление: Я попробовал обобщенные элементы с верхней границей, но моя проблема не решена,Пожалуйста, найдите код ниже:

// Using generics doesn't allow me to pass A.
open class A (val i: Int) {
    fun <M: A> g(x: (M)->Int){
        print(x(this)) // Error: Type mismatch. Expected: M, Found: A 
    }
}

Ответы [ 2 ]

2 голосов
/ 08 апреля 2019

То, что вы пытаетесь сделать, это преобразование из типа функции (B) -> Int ( источник ) в (A) -> Int ( target ). Это не безопасное преобразование.

Ваша исходная функция (B) -> Int принимает любой экземпляр, который является B, , но не обязательно экземпляр типа A. Более конкретно, он не может обрабатывать все аргументы типа A, но не типа B.

Представьте, что ваши занятия выглядят так:

open class A
class B : A {
    fun onlyB() = 29
}

Вы можете определить функцию

val fb: (B) -> Int = { it.onlyB() }
val fa: (A) -> Int = fb // ERROR

Функция fb не сможет работать с классом A, поскольку A не имеет функции onlyB(). Как следствие, вам не разрешено преобразовывать его в тип функции, который принимает A параметров.


Эта концепция называется Контравариантность , что означает, что входные параметры могут быть ограничены только путем превращения более конкретным , а не более абстрактным. Итак, работает обратное направление:

val fa: (A) -> Int = { it.hashCode() }
val fb: (B) -> Int = fa // OK, every B is also an A

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

val fInt: (B) -> Int = { it.onlyB() }
val fNum: (B) -> Number = fInt // OK, every Int is also a Number

Эти отношения могут быть использованы в общих классах с использованием ключевых слов Kotlin in (контрвариантность) и out (ковариантность) - см. здесь для подробного объяснения.

2 голосов
/ 08 апреля 2019

Вы можете решить эту проблему, используя generics и функцию расширения на универсальном приемнике.Вывод функции расширения из вашего обновленного образца:

fun <T : A> T.g(x: (T)->Int){
    print(x(this))
}

Таким образом гарантируется, что получатель и первый тип параметра данной функции одинаковы, что является либо A, либо подтипомэто.

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