2016-01-12 8 views
8

Mentre meditando attraverso il codice di Predef.scala ho notato quanto segue:DummyImplicits .... è usato ??? E come?

/** A type for which there is always an implicit value. 
* @see [[scala.Array$]], method `fallbackCanBuildFrom` 
*/ 
class DummyImplicit 

object DummyImplicit { 

    /** An implicit value yielding a `DummyImplicit`. 
    * @see [[scala.Array$]], method `fallbackCanBuildFrom` 
    */ 
    implicit def dummyImplicit: DummyImplicit = new DummyImplicit 
} 

fa comunque avere un indizio perché questo pezzo di codice apparentemente inutile esiste ???

+4

Sei andato al codice sorgente di 'scala.Array $' metodo 'fallbackCanBuildFrom' come suggerisce il commento? Forse ti dà la più pallida idea di cosa sia. – Jesper

risposta

18

In fin dei conti si tratta di type erasure (utilizzato sia da Java che da Scala).

Immaginate questo codice:

object Foo { 
    def foo(p: String) = 1 
    def foo(p: Int) = 2 
    def foo(p: Any) = 3 
} 

object Main extends App { 
    Foo.foo("1") 
} 

Tutto è bene qui. Ma cosa succede se cambiamo i parametri da singoli valori a una sequenza?

object Foo { 
    def foo(ps: String*) = 1 
    def foo(ps: Int*) = 2 
    def foo(ps: Any*) = 3 
} 

object Main extends App { 
    Foo.foo("1") 
} 

Ora abbiamo un errore:

Main.scala:4: error: double definition: 
def foo(ps: Int*): Int at line 3 and 
def foo(ps: Any*): Int at line 4 
have same type after erasure: (ps: Seq)Int 
     def foo(ps: Any*) = 3 
     ^
Main.scala:3: error: double definition: 
def foo(ps: String*): Int at line 2 and 
def foo(ps: Int*): Int at line 3 
have same type after erasure: (ps: Seq)Int 
     def foo(ps: Int*) = 2 
     ^
two errors found 

E vedere il messaggio "hanno lo stesso tipo dopo la cancellazione" - questo è il nostro indizio.

Quindi, perché la sequenza ha avuto esito negativo?

Poiché la JVM non supporta i generici, ciò significa che le vostre raccolte di tipi fortemente (come la sequenza di interi o stringhe), in realtà non lo sono. Sono compilati in contenitori di Object perché è quello che si aspetta la JVM. I tipi sono "cancellati".

Così, dopo la compilazione di tutti questi simile a questa (vedremo esattamente quello che sono in un momento):

object Foo { 
    def foo(ps: Object*) = 1 
    def foo(ps: Object*) = 2 
    def foo(ps: Object*) = 3 
} 

Ovviamente non è questo ciò che si intendeva.

Quindi, come si comporta Java con questo?

Crea Bridge Methods dietro le quinte. Magia!

Scala non fa quella magia (sebbene sia stata discussed) - piuttosto usa impliciti fittizi.

Cambiamo la nostra definizione

object Foo { 
    def foo(ps: String*) = 1 
    def foo(ps: Int*)(implicit i: DummyImplicit) = 2 
    def foo(ps: Any*)(implicit i1: DummyImplicit, i2: DummyImplicit) = 3 
} 

E ora compila! Ma perché?

Guardiamo il codice che ha generato scalac (scalac foo.scala -print)

object Foo extends Object { 
    def foo(ps: Seq): Int = 1; 
    def foo(ps: Seq, i: Predef$DummyImplicit): Int = 2; 
    def foo(ps: Seq, i1: Predef$DummyImplicit, i2: Predef$DummyImplicit): Int = 3; 
    def <init>(): Foo.type = { 
     Foo.super.<init>(); 
    () 
    } 
    }; 

OK - quindi abbiamo tre metodi Foo distinte che differiscono solo per i loro parametri impliciti.

E ora chiamiamoli così:

object Main extends App { 
    Foo.foo("1") 
    Foo.foo(1) 
    Foo.foo(1.0) 
} 

E questo cosa assomigliano (sto togliendo un sacco di altro codice qui ...)

Foo.foo(scala.this.Predef.wrapRefArray(Array[String]{"1"}.$asInstanceOf[Array[Object]]())); 
    Foo.foo(scala.this.Predef.wrapIntArray(Array[Int]{1}), scala.Predef$DummyImplicit.dummyImplicit()); 
    Foo.foo(scala.this.Predef.genericWrapArray(Array[Object]{scala.Double.box(1.0)}), scala.Predef$DummyImplicit.dummyImplicit(), scala.Predef$DummyImplicit.dummyImplicit()); 

Così ogni chiamata è stato dato il parametro implicito necessario per disambiguare correttamente la chiamata.

Quindi perché esiste DummyImplicit? Per assicurarti che ci sia un tipo per il quale ci sarà sempre un valore implicito (altrimenti dovresti assicurarti che fosse disponibile).

La documentazione indica "Un tipo per il quale esiste sempre un valore implicito". - quindi esiste sempre il valore implicito da utilizzare in casi come questo.

+7

Spiegate perfettamente il loro uso più comune, ma ironicamente non citate la ragione originale per cui sembra essere stata introdotta, come suggerito nel commento di 'DummyImplicit', che suggerisce il suo uso in' Array.fallbackCanBuildFrom'. Lì, è usato per controllare l'ordine di ricerca implicita. Inoltre, per quanto ne so, lo scopo dei metodi bridge di Java non è quello di risolvere lo stesso problema come discusso qui. –

Problemi correlati