在Scala中重载通用方法

时间:2016-02-24 12:23:47

标签: scala generics overloading

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

我要做的是在类map中实现两个A方法。这两个方法都接受一个闭包,但对于其中一个,闭包的返回值的类型是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
    }
}

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

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

希望有人对这个问题感兴趣,并为我提供更好的设计。提前谢谢。

2 个答案:

答案 0 :(得分:0)

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

在这种特定情况下,可以推断x的类型为Int,无论选择哪个map函数,但遗憾的是编译器无法确定这一点。

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

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

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

答案 1 :(得分:0)

我不知道如何使用方法重载来做到这一点。问题是类型推断器在这种情况下不起作用。您可以查看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))

    }
}