2010-05-02 73 views
9
scala> val shares = Map("Apple" -> 23, "MicroSoft" -> 50, "IBM" -> 17) 
shares: scala.collection.immutable.Map[java.lang.String,Int] 
     = Map(Apple -> 23, MicroSoft -> 50, IBM -> 17) 

scala> val shareholders = shares map {_._1}       
shareholders: scala.collection.immutable.Iterable[java.lang.String] 
      = List(Apple, MicroSoft, IBM) 

scala> val newShares = shares map {case(k, v) => (k, 1.5 * v)}  
newShares: scala.collection.immutable.Map[java.lang.String,Double] 
     = Map(Apple -> 34.5, MicroSoft -> 75.0, IBM -> 25.5) 

从这个例子看来,map方法在返回类型上被重载。在返回类型上重载是不可能的吧?请有人解释一下这里发生了什么?返回类型重载?

回答

11

map不会在返回类型上重载。相反,有一种抽象返回类型的方法。

def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That 

在字节码,这是擦除Object

public abstract java.lang.Object map(scala.Function1, scala.collection.generic.CanBuildFrom); 

在相纸上的最佳描述的模式Fighting Bit Rot with Types

+2

这是自从我开始编程是我所见过最可怕的事情。 – 2010-05-02 15:12:38

+3

编写这种'map'方法很困难,但通常很容易使用。 – retronym 2010-05-02 17:49:35

+1

@GreenHyena只为PHP开发人员:) – expert 2013-04-30 02:56:00

4

你可能想看看this questionmap签名,其具有参数返回类型That。 Martin Odersky的答案解释了可以如何(以及为什么)返回不同的类型。

请注意,返回类型没有以任何方式绑定Traversable,或者甚至是目标的参数化类型。例如:

IndexedSeq[Char].map --> String 

如可以通过看StringOps

+0

感谢您的链接。虽然什么都不明白。几个月后再次检查该线程,当时我希望已经成为更好的标量。 :-) – 2010-05-02 15:14:27

4

这是不是发生了什么事情在这种情况下,但实际上是,overloading on return type is supported by JVM可见。这在Scala中公开了具有不同泛型参数类型的擦除类型相同的方法。链接上给出的例子是

object Overload{ 
    def foo(xs : String*) = "foo"; // after erasure is foo:(Seq)String 
    def foo(xs : Int*) = 3;  // after erasure is foo:(Seq)Int 
} 
0

这是一个从我正在写的游戏中取得的例子。它覆盖的返回类型:

def consumeItem(item: ConsumableItem) { 
    executePartyAction[Unit](_.inventory.consumeItem(item, this)) 
} 

def craftItem[ItemType <: Item](recipe: Recipe[ItemType]) = { 
    executePartyAction[ItemType](_.inventory.craftItem(recipe, this)) 
} 

private def executePartyAction[ReturnType](partyAction: Party => ReturnType): ReturnType = { 
    party match { 
     case Some(party) => partyAction(party) 
     case None => throw new PlayerCharacterMustBelongToAParty 
    } 
} 
+1

这不会基于方法的返回类型重载方法。它根据传递给方法的函数的返回类型(即方法本身的参数类型)重载函数。 – 2010-05-06 04:09:37

2

虽然the truth is complicated,我要去contibute适用同样的原则,但简化了很多东西,所以你可以看到在语言逻辑的讨论。

Scala没有基于返回类型的重载。 (即使在模式匹配在“参数”的模式匹配了与返回类型unapply,使其能够使用返回类型,以解决过载。)

你不超载map基于返回类型的方法 - 基于作为参数传递的函数的返回类型来重载它。返回类型的变化会改变参数的返回类型,所以基本上根据不同的参数类型进行重载。所以从逻辑上来说,你有什么等同于以下情况:

trait Seq[X]{ 
    def map[Y](func: X => Y) : Seq[Y] 
    def map[Y,Z](func: X => Tuple2[Y,Z]) : Map[Y,Z] 
} 

传递到地图的函数的返回类型决定哪个版本被调用。

real implementation只是使这种逻辑更加通用和可扩展到许多Scala库中的集合类型,以及许多尚未编写的其他集合类型。

0

随着类型类,具有不同的返回类型超载可以实现:

object AdHocOverloading extends App { 

    implicit class UnboxOps[T, R, B](b: B)(implicit ev: UnboxEv[T, B, R], ev1: B <:< Box[T]) { 
    def value: R = ev.unbox(b) 
    } 

    val optional = Box(Some(3)) 
    val confident = new Box(Some("C")) with Confidence 
    val otherType = Seq("bad") 

    optional.value 
    confident.value 

    //otherType.value //compile time error 

    println(optional.value) 
    //Some(3) 
    println(confident.value) 
    //C 
} 

trait UnboxEv[+T, -B, +R] { 
    def unbox(b: B): R 
} 

trait Confidence 
case class Box[+T](v: Option[T]) //v could be private 
trait LowLevelImplicitOfBox { 
    this: Box.type => 
    implicit def optionEvidence[T]: UnboxEv[T, Box[T], Option[T]] = 
    new UnboxEv[T, Box[T], Option[T]] { 
     override def unbox(b: Box[T]): Option[T] = b.v 
    } 
} 
object Box extends LowLevelImplicitOfBox { 
    implicit def confidentEvidence[T]: UnboxEv[T, Box[T] with Confidence, T] = 
    new UnboxEv[T, Box[T] with Confidence, T] { 
     override def unbox(b: Box[T] with Confidence): T = b.v.get 
    } 
} 

例如来自:https://github.com/cuzfrog/scala-points#29-ad-hoc-overloading-monkey-patch-method-with-different-return-type