2010-05-02 14 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) 

En este ejemplo, parece que el método map está sobrecargado en el tipo de devolución. La sobrecarga en el tipo de retorno no es posible, ¿verdad? ¿Alguien podría explicar lo que está pasando aquí?¿Sobrecarga en el tipo de devolución?

Respuesta

11

map no está sobrecargado en el tipo de devolución. En cambio, hay un método con un tipo de retorno abstracto.

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

En el código de bytes, este se borra a Object:

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

El patrón es el mejor descrito en el documento Fighting Bit Rot with Types

+2

Esta es la cosa más espantosa que he visto desde que empecé programación. –

+3

Es difícil escribir este método de 'mapa', pero en general es muy fácil de usar. – retronym

+1

@GreenHyena solo para desarrolladores de PHP :) – expert

4

Es posible que desee mirar a this question sobre la firma de map cuales tiene un tipo de retorno paramétrico That. La respuesta de Martin Odersky explica cómo (y por qué) se pueden devolver diferentes tipos.

Tenga en cuenta que el tipo de devolución no está vinculado de ninguna manera a Traversable, o incluso el tipo parametrizado del destino. Por ejemplo:

IndexedSeq[Char].map --> String 

Como puede verse mirando StringOps

+0

Gracias por el enlace. No entendí nada sin embargo. Verificará ese hilo nuevamente después de unos meses, cuando espero haberme convertido en un escalar mejor. :-) –

4

esto no es lo que está pasando en este caso, pero en realidad sí, overloading on return type is supported by JVM. Esto está expuesto en Scala para los métodos que tienen diferentes tipos de argumentos genéricos que se borran al mismo tipo. El ejemplo dado en el enlace es

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

Aquí hay un ejemplo tomado de un juego que estoy escribiendo. Se anula el tipo de retorno:

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

Esto no sobrecarga un método basado en el tipo de devolución del método. Sobrecarga una función basada en el tipo de retorno de una función que se pasa al método, es decir, el tipo de parámetro del método en sí. –

2

Mientras the truth is complicated, voy a contibute una discusión que se aplica el mismo principio, pero simplifica mucho las cosas para que pueda ver la lógica en el lenguaje.

Scala no tiene sobrecarga según el tipo de devolución. (Ni siquiera en la coincidencia de patrones, donde los "parámetros" para el ajuste de patrones con el tipo retorno del unapply, por lo que es posible utilizar el tipo de retorno para resolver la sobrecarga.)

Usted no está sobrecargando el map método basado en el tipo de devolución: lo está sobrecargando en función del tipo de devolución de la función que se pasa como parámetro. El cambio en el tipo de devolución cambia el tipo de devolución del parámetro, por lo que básicamente está sobrecargando según diferentes tipos de parámetros. Por lo que lógicamente hablando, usted tiene algo equivalente a la siguiente situación:

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

El tipo de retorno de la función pasada al mapa determina qué versión se llama.

El real implementation simplemente hace que esta lógica sea más general y extensible a muchos tipos de colecciones que están en la biblioteca de Scala, y a muchos otros tipos de colecciones que aún no se han escrito.

0

Con clase de tipos, sobrecargando con diferente tipo de retorno podría lograrse:

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

ejemplo de: https://github.com/cuzfrog/scala-points#29-ad-hoc-overloading-monkey-patch-method-with-different-return-type

Cuestiones relacionadas