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 -&开发者_开发问答gt; 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)
From this example it seems like the map
method is overloaded on return type. Overloading on return type is not possible right? Would somebody please explain what's going on here?
map
isn't overloaded on return type. Instead, there is one method with an abstract return type.
def map[B, That](f: A => B)(implicit bf: CanBuildFrom[Repr, B, That]): That
In the bytecode, this is erased to Object
:
public abstract java.lang.Object map(scala.Function1, scala.collection.generic.CanBuildFrom);
The pattern is described best in the paper Fighting Bit Rot with Types
This isn't what's going on in this case, but actually yes, overloading on return type is supported by JVM. This is exposed in Scala for methods which have different generic argument types which erase to the same type. The example given at the link is
object Overload{
def foo(xs : String*) = "foo"; // after erasure is foo:(Seq)String
def foo(xs : Int*) = 3; // after erasure is foo:(Seq)Int
}
You might want to look at this question about the signature of map
which has a parametric return type That
. Martin Odersky's answer explains how (and why) different types may be returned.
Note that the return type is not bound in any way to Traversable
, or even the parametrized type of the target. For example:
IndexedSeq[Char].map --> String
As can be seen by looking at StringOps
While the truth is complicated, I'm going to contibute a discussion that applies the same principle but simplifies things a lot so you can see the logic in the language.
Scala doesn't have overloading based on return type. (Not even in pattern matching where the "parameters" to the pattern match up with the return type of the unapply
, making it possible to use the return type to resolve the overload.)
You're not overloading the map
method based on return type -- you're overloading it based on the return type of function that's passed as a parameter. The change in return type changes the return type of the parameter, so you're essentially overloading based on different paramter types. So logically speaking, you have something equivalent to the following situation:
trait Seq[X]{
def map[Y](func: X => Y) : Seq[Y]
def map[Y,Z](func: X => Tuple2[Y,Z]) : Map[Y,Z]
}
The return type of the function passed to map determines which version is called.
The real implementation just makes this logic more general and extensible to lots of collection types that are in the Scala library, and to lots of other collection types that haven't been written yet.
With typeclass, overloading with different return type could be achieved:
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
}
}
example from:https://github.com/cuzfrog/scala-points#29-ad-hoc-overloading-monkey-patch-method-with-different-return-type
Here's an example taken from a game I'm writing. It overrides the return type:
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
}
}
精彩评论