2016-02-24 135 views
3

我是Scala的新手,我遇到了一個我無法在線找到解決方案的問題。所以我在這裏尋求幫助。在Scala中重載泛型方法

我想要做的是在類A中實現兩個map方法。這兩個方法都接受一個閉包,但對於其中的一個,閉包的返回值的類型是Tuple2。另外兩個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 
    } 
} 

A類和B和兩個map方法的定義是可以接受通過我的Scala的計算機上。問題來了。正如我在main方法中顯示的,我如何使用類A中的映射方法,前兩個語句導致編譯錯誤(在閉包中說缺少參數類型x),並且只有最後一個是可執行的。

如果我刪除類A中的第二個map方法,則前兩個語句將變爲可執行文件。我不知道爲什麼以及我應該怎麼做。我只想保持這兩種方法共享相同的名稱map,同時我不需要告訴使用map方法時閉包的參數類型。

希望任何人對這個問題感興趣,並提供給我一個更好的設計。提前致謝。

+0

你確定要重載'map'函數嗎?你所要求的是很難在scala中實現的。 – vitalii

+0

一些觀點:(i)Scala具有泛型而不是模板 - 請參閱[這裏](http://stackoverflow.com/a/498329/4070984)討論差異。 (ii)你在談論的是方法而不是函數,這在Scala中是非常不同的(函數是對象,不能被重載或泛型) - 請參閱[這裏](http://stackoverflow.com/a/2530007/4070984)。我已提交修改以糾正這些問題。 – Ben

+0

@vitalii是的,我只是想'map'函數在不同情況下返回不同的類型(取決於閉包的返回值的類型)。如果我可以得到類「B」的對象,那麼我可以確定存儲在類「B」中的值的類型是一個「Tuple2」,我也可以獲得鍵和值的類型。 –

回答

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)) 

    } 
} 
+0

0.0非常感謝這個例子! –

0

編譯器想要確定x的類型,爲了這樣做,它必須首先確定使用了兩個map函數中的哪一個。但是它不知道使用哪個映射函數,除非參數的類型是已知的。因此編譯器不能這樣做。

在這種特殊情況下,可以推斷x的類型爲Int,而不管選擇哪個map函數,但不幸的是編譯器無法確定這一點。

什麼是可能的,是明確指定地圖功能的類型參數。這樣,編譯器知道要使用哪個函數(和什麼類型的參數)

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

另一種方法是給第二map功能不同的名稱,因爲它的簽名是從map是如何通常定義不同。

+0

謝謝,但我不想顯示map函數的類型參數。我認爲vitalii的答案是我想要的。 –