【问题标题】:Using multiple generic types in lambda in kotlin在 kotlin 的 lambda 中使用多个泛型类型
【发布时间】:2018-05-23 01:50:34
【问题描述】:
Koltin 1.2.30

我正在使用泛型和 lambda 函数。

以下工作没有泛型类型

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

    num1 + num2
}

但是,对于泛型:

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

    num1 + num2
}

我猜上面的方法是行不通的,因为泛型类型可能不是数字类型,有人可以传入一个字符串,如果涉及计算,lambda 将不知道如何处理字符串类型。

关于如何传入多个泛型并返回泛型类型的任何示例建议?

非常感谢,

【问题讨论】:

  • 当我读到你的问题时,它是关于对 lambda 和泛型的一般理解,对吧?或者你想达到什么具体的目标?无论如何,我用下面的典型用例写了一个通用答案。

标签: generics lambda kotlin


【解决方案1】:

你是对的:泛型,当你使用它们时,允许使用任何类型,即使是那些不提供 + 运算符的类型。将类型指定为Number 很容易。但是,它也不会使函数编译:

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

(请注意,您的示例中不需要第二个类型参数R。)

问题再次是即使Number 也没有在其合约中包含+ 运算符。这些是为IntDouble 等特定类型直接定义的(参见source)。

像 Kotlin 这样的强类型语言不允许这样的实现。

【讨论】:

    【解决方案2】:

    没有任何合理的方式来写任何带有签名的东西

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

    因为它说调用者可以选择任何TR,然后你需要从两个Ts中获取一个R;但是TR之间没有关系,所以不能使用Ts。

    你可以让它编译:例如使用someExpression as R,或throw SomeException(),或无限循环。但这些都不是真正有用的。

    一般来说(C++ 是一个很大的例外;它的模板工作方式非常不同),泛型方法必须:

    1. 适用于所有类型(或者,在 Java 中,适用于除原语以外的所有类型)。所以它只能使用所有类型上可用的操作,而且很少。一个有两个类型参数的例子是

      fun <T1, T2> apply(f: T1 -> T2, x: T1) = f(x)
      
    2. 适用于满足某些约束的所有类型。不同的语言允许不同种类的约束;例如C# 有一个相当大的集合,Scala 有视图和上下文边界。据我所知,Kotlin 只允许upper bounds。特别是,没有“type T has operator +”的约束。所以写不出你想要的函数。

    【讨论】:

    • 这实际上是解决问题中基本问题的唯一答案,我认为一旦添加有关如何使用泛型类型约束的提示,它就值得被接受。
    • 我会说它处理 a 基本问题。但你说得对,我应该扩展它。
    • 现在我做到了(也涵盖了其他问题)。
    • 您可以将类型约束到 Kotlin 中的接口:interface Item { fun bar(): Int }class ItemManager&lt;T: Item&gt; {}
    • @GaryWright 是的,这在最后一段中有所介绍。
    【解决方案3】:

    对于一个工作示例,lambda 主体只能调用对其超类型保证存在的泛型值的操作(正如您已经说过的)。所以一些非常通用的工作示例可能是:

    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
    }
    

    有趣的事实:虽然可以从函数返回泛型 lambda,但它们不能(通常)存储在字段或变量中:

    val f = onScanExt2()
    

    编译失败,报错:

    没有足够的信息来推断参数 T [..]。请明确说明。

    非常不同于纯粹的函数式语言,比如古老的 Haskell,函数和包含函数的变量之间实际上没有这种区别。如果你喜欢简单,那么不妨试试 Haskell!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-06-02
      • 2017-12-06
      • 1970-01-01
      • 2016-09-28
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多