【问题标题】:Overloading generic methods in Scala在 Scala 中重载泛型方法
【发布时间】:2016-02-24 12:23:46
【问题描述】:

我是 Scala 新手,遇到了一个无法在线找到解决方案的问题。所以我在这里寻求帮助。

我要做的是在A 类中实现两个map 方法。这两个方法都接受一个闭包,但是对于其中一个,闭包的返回值的类型是Tuple2。两个map 方法的返回值也不属于同一类(这就是为什么我需要两个'map' 方法)。代码简化如下:

object Test {
    class A[T] {
        def map[V](a: T => V): A[V] = {
            null
        }
        def map[K, V](a: T => Tuple2[K, V]): B[K, V] = {
            null
        }
    }

    class B[K, V] extends A[Tuple2[K, V]] {}

    def main(args: Array[String]) {
        new A[Int].map(x => x + 1) //compile error
        new A[Int].map{x => x + 1} //compile error
        new A[Int].map{x:Int => x + 1} //passed but ugly in use
    }
}

AB 以及两个map 方法的定义在我的计算机上是Scala 可以接受的。问题来了。正如我在main 方法中展示的我如何在A 类中使用映射方法,前两个语句导致编译错误(在闭包中说缺少x 的参数类型)并且只有最后一个语句是可执行的。

如果我删除A 类中的第二个map 方法,前两个语句将变为可执行。我不知道为什么以及我应该怎么做。我只想让map的两个方法保持同名,同时在使用map方法时不需要告诉闭包的参数类型。

希望任何对此问题感兴趣的人,并为我提供更好的设计。提前致谢。

【问题讨论】:

  • 您确定要重载map 函数吗?你问的问题很难在 scala 中实现。
  • 几个方面:(i) Scala 有泛型而不是模板 - 请参阅 here 以讨论差异。 (ii) 你说的是方法而不是函数,它们在 Scala 中是非常不同的(函数是对象,不能重载或泛型) - 请参阅 here。我已提交修改以更正这些要点。
  • @vitalii 是的,我只是希望map 函数在不同的情况下返回不同的类型(取决于闭包返回值的类型)。如果我可以得到B 类的对象,那么我可以确定存储在B 类中的值的类型是Tuple2,我还可以得到键和值的类型。
  • @Ben 我已经使用 C++ 4 到 5 年了,我确实认为 Scala 中的泛型等同于 C++ 中的模板。感谢您的指正。

标签: scala generics overloading


【解决方案1】:

我不知道如何使用方法重载来做到这一点。问题是类型推断器在这种情况下不起作用。您可以查看magnet pattern,但您仍然需要指定完整的类型。相反,我使用了一种在 scala 集合中使用的方法。请注意,我不知道仍然可能存在一些极端情况。看看:

object Example {
    class A[T] {
        def map[B, That](f: T => B)(implicit bf: CanBuildFrom[A[T], B, That]): That = {
            val res = f(???)
            bf(res)
        }

    }

    class B[K, V] extends A[Tuple2[K, V]] {}


    trait CanBuildFrom[-From, -Elem, +To] {
      def apply(el: Elem) : To
    }

    def main(args: Array[String]) {

        implicit def ev0[T,R] = new CanBuildFrom[A[T], R, A[R]] {
          def apply(el : R) : A[R] =  new A()
        }

        implicit def ev1[T,K,V] = new CanBuildFrom[A[T], Tuple2[K,V], B[K,V]] {
          def apply(el :(K,V)) : B[K,V] =  new B()
        }

        val res1 : B[Int, Int] = new A[Int].map(x => (2,3))
        val res2 : A[Int] = new A[Int].map { x => 1 }

        new A[Int].map { x => 1 }
        new A[Int].map(x => (2,3))

    }
}

【讨论】:

  • 0.0 非常感谢这个例子!
【解决方案2】:

编译器想要确定 x 的类型,为了做到这一点,它必须首先确定使用了两个 map 函数中的哪一个。但是,除非参数的类型已知,否则它无法知道使用了哪个映射函数。因此编译器不能这样做。

在这种特定情况下,可以推断 x 的类型将是 Int,无论选择哪种映射函数,但不幸的是编译器无法确定这一点。

什么是可能的,显式指定 map 函数的类型参数。这样编译器就知道要使用哪个函数(以及什么类型的参数)

new A[Int].map[Int](x => x + 1)
new A[Int].map[Int, String]{x => (x, "1")}

另一种选择是给第二个 map 函数一个不同的名称,因为它的签名与通常定义的 map 不同。

【讨论】:

  • 谢谢,但我也不想显示 map 函数的类型参数。我认为vitalii的答案是我想要的。
猜你喜欢
  • 1970-01-01
  • 2011-06-26
  • 2011-11-13
  • 1970-01-01
  • 2013-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-16
相关资源
最近更新 更多