在返回类型上重载?

时间:2010-05-02 08:31:52

标签: scala

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方法似乎在返回类型上重载。在返回类型上重载是不可能的?有人可以解释一下这里发生了什么吗?

6 个答案:

答案 0 :(得分:12)

返回类型

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

中有最佳描述

答案 1 :(得分:5)

这不是在这种情况下发生的事情,但实际上是,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
}

答案 2 :(得分:4)

您可能希望查看this questionmap签名的参数返回类型That。 Martin Odersky的回答解释了如何(以及为什么)可以返回不同的类型。

请注意,返回类型不以任何方式绑定到<{em>到Traversable,甚至不是目标的参数化类型。例如:

IndexedSeq[Char].map --> String

通过查看StringOps

可以看出

答案 3 :(得分:3)

虽然the truth is complicated,但我会讨论一个适用同样原则的讨论,但会简化很多事情,以便你可以看到语言中的逻辑。

Scala没有基于返回类型的重载。 (甚至在模式匹配中,模式的“参数”与unapply返回类型匹配,从而可以使用返回类型来解决重载。)< / p>

您没有根据返回类型重载map方法 - 您根据作为参数传递的函数的返回类型重载它。返回类型的更改会更改参数的返回类型,因此您基本上会根据不同的参数类型进行重载。从逻辑上讲,你有类似下列情况的东西:

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

传递给map的函数的返回类型决定了调用哪个版本。

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

答案 4 :(得分:1)

使用类型类,可以实现不同返回类型的重载:

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

答案 5 :(得分: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
    }
}