Использование нескольких универсальных типов в лямбда-выражениях в kotlin - PullRequest
0 голосов
/ 23 мая 2018
Koltin 1.2.30

Я работаю с универсальными и лямбда-функциями.

Следующие операции без универсальных типов

fun onScanExt(): (Int, Int) -> Int = {
    num1, num2 -> num1 + num2

    num1 + num2
}

Однако с универсальными типами:

fun <T, R> onScanExt(): (T, T) -> R = {
    num1, num2 -> num1 + num2

    num1 + num2
}

Полагаю, что вышеописанное не может работать, так как универсальный тип может не являться типом Number, и кто-то может передать String, и лямбда не будет знать, что делать с типом String, если в него вовлечены вычисления.

Какие-нибудь примеры примеров того, как передать несколько обобщенных типов и вернуть универсальный тип?

Заранее большое спасибо,

Ответы [ 3 ]

0 голосов
/ 27 мая 2018

В рабочем примере лямбда-тело может вызывать операции только с общими значениями, которые их супертип гарантирует (как вы уже сказали).Так что некоторые очень общие рабочие примеры могут быть:

fun <T> onScanExt(): (T, T) -> String  = { a, b ->
    a.toString() + b.toString()
}

Или:

fun <T: Comparable<T>> onScanExt(): (T, T) -> T  = { a, b ->
    if (a.compareTo(b) < 0) a else b
}

Интересный факт: хотя общие лямбда-выражения можно возвращать из функций, их нельзя (в общем случае) хранить в поляхили переменные:

val f = onScanExt2()

Не удается скомпилировать с ошибкой:

Недостаточно информации для вывода параметра T [..].Пожалуйста, укажите это явно.

Очень сильно отличается от чисто функциональных языков, таких как старый добрый Haskell, где действительно нет такой разницы между функциями и переменными, содержащими функции.Если вам нравится это просто, то, возможно, попробуйте Haskell!

0 голосов
/ 27 мая 2018

Нет разумного способа написать что-нибудь с подписью

fun <T, R> onScanExt(): (T, T) -> R

, потому что он говорит, что вызывающий абонент может выбрать любой T и R, а затем вам нужно получить R из двухT s;но между T и R нет никакой связи, поэтому вы не можете использовать T s.

. Вы можете сделать это скомпилированным: например, используйте someExpression as R, или throw SomeException(), илибесконечный цикл.Но ни один из них на самом деле не полезен.

В целом (C ++ здесь является большим исключением; его шаблоны работают совсем по-другому), универсальный метод должен либо:

  1. Работана всех типах (или в Java на всех типах, кроме примитивов).Таким образом, он может использовать только операции, доступные для всех типов, и их очень мало.Примером с двумя параметрами типа будет

    fun <T1, T2> apply(f: T1 -> T2, x: T1) = f(x)
    
  2. Работа со всеми типами, которые удовлетворяют некоторым ограничениям.Разные языки допускают разные виды ограничений;Например, C # имеет довольно большой набор, Scala имеет границы просмотра и контекста.Насколько мне известно, Котлин допускает только верхние границы .В частности, нет ограничения, которое говорит, что «тип T имеет оператор +».Таким образом, нужная вам функция не может быть написана.

0 голосов
/ 23 мая 2018

Вы правы: дженерики, как вы их использовали, позволяют использовать любой тип, даже те, которые не предлагают оператор +, например.Определить тип как Number легко.Тем не менее, это не приведет к компиляции функции:

fun <T : Number> onScanExt(): (T, T) -> T = { 
    num1, num2 -> num1 + num2
}

(обратите внимание, что параметр второго типа R не требуется в вашем примере.)

Проблема снова заключается в том, чтодаже Number не включает оператора + в свой контракт.Они определены для конкретных типов Int, Double и так далее (см. source ).

A язык со строгой типизацией , такой как Kotlin, не будетразрешить такую ​​реализацию.

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