2013-04-30 18 views
5

Qualcuno può chiarire il motivo per cui il codice seguente provoca un errore MatchError? Che cosa significa MatchError in questo caso?Sovrascrittura dei valori dell'oggetto companion e Scala MatchError

class A { 
    def g = A.f 
} 

object A { 
    val f = "Object A" 
} 


class B extends A { 
    override val A.f = "Object B" 
} 

val b = new B 
b.g 

Dato che non funziona, esiste un modo per ignorare un oggetto associato val o def simile a questo?

risposta

3

In primo luogo, perché si vede uno MatchError. Un valore su un oggetto (A.f) è considerato un identificatore stabile (come dice il riferimento di Scala, "I membri stabili sono [...] membri introdotti da definizioni di oggetti o da definizioni di valori di tipi non volatili").

Ecco cosa l'uscita typer assomiglia:

object A extends scala.AnyRef { 
    ... 
    private[this] val f: String = "Object A"; 
    <stable> <accessor> def f: String = A.this.f 
} 

Quando utilizzato in un incarico, il compilatore "desugars" assegnazione di questo identificatore stabile (che sia stabile è una condizione necessaria) nel pattern matching:

<synthetic> private[this] val x$1: Unit = ("Object B": String("Object B") @unchecked) match { 
    case A.f =>() 
} 

Non può corrispondere "Oggetto B" a un motivo "Oggetto A", quindi genera uno MatchError.

Alla tua domanda più grande: non puoi/non deve sovrascrivere valori e metodi su un oggetto associato. Il polimorfismo si applica alle classi e alle relative istanze, non ai metodi o ai valori statici. C'è probabilmente un modo migliore di pensare al tuo programma che non coinvolga vals/defs sovrascrivibili su un oggetto companion.

+0

Grazie per il chiarimento! Concordo con te sulla necessità di ignorare gli oggetti companion in generale, ma la ragione per cui voglio farlo qui è a scopo di test, dove voglio sostituire un metodo all'interno dell'oggetto companion con un altro metodo. Inoltre, alcuni linguaggi consentono l'override dei metodi statici. – deepkimo

1

Questo è interessante! Se si mettono le definizioni delle classi in un file e compilarlo usando scalac -print test.scala si vedrà qualcosa di simile,

[[syntax trees at end of     cleanup]] // scala 
package <empty> { 
    class A extends Object { 
    def g(): String = A.f(); 
    def <init>(): A = { 
     A.super.<init>(); 
    () 
    } 
    }; 
    object A extends Object { 
    private[this] val f: String = _; 
    <stable> <accessor> def f(): String = A.this.f; 
    def <init>(): A.type = { 
     A.super.<init>(); 
     A.this.f = "Object A"; 
    () 
    } 
    }; 
    class B extends A { 
    <synthetic> private[this] val x$1: runtime.BoxedUnit = _; 
    def <init>(): B = { 
     B.super.<init>(); 
     B.this.x$1 = { 
     case <synthetic> val x1: String = ("Object B": String("Object B")); 
     case5(){ 
      if (A.f().==(x1)) 
      { 
       val x2: String = x1; 
       matchEnd4(scala.runtime.BoxedUnit.UNIT) 
      } 
      else 
      case6() 
     }; 
     case6(){ 
      matchEnd4(throw new MatchError(x1)) 
     }; 
     matchEnd4(x: runtime.BoxedUnit){ 
      scala.runtime.BoxedUnit.UNIT 
     } 
     }; 
    () 
    } 
    } 
} 

Questo dimostra che il compilatore crea una classe B con un'inizializzazione che esegue una partita controllo per vedere che il valore hai usato in override il val A.f è uguale al valore originale if (A.f().==(x1)). Non sembra essere troppo utile vero? Se non sono uguali, genera l'errore di corrispondenza chiamando case6(). Possiamo confermarlo modificando la definizione dello class B in override val A.f = "Object A".

class B extends A { 
     override val A.f = "Object A" 
} 

scala> val b = new B 
b: B = [email protected] 

Quindi come ripararlo? Si può solo fare questo,

class B extends A { 
     override def g = "Object B" 
} 
scala> val b = new B 
b: B = [email protected] 

scala> b.g 
res1: String = Object B 

o

class B extends A { 
     val f = "Object B" 
} 
scala> val b = new B 
b: B = [email protected] 

scala> b.f 
res0: String = Object B 
+0

Puoi prendere in considerazione l'utilizzo dei tratti: http://stackoverflow.com/a/7625150/1274818 – tysonjh

+0

Le tue correzioni sono un modo intelligente per aggirare questo semplice esempio di codice, ma non quello che sto cercando, dal momento che voglio chiamare il metodo Af originale. – deepkimo

+0

Sì, i tratti sono un buon modo per strutturare questo, ma nel mio caso voglio testare il codice che esiste già senza cambiare il codice principale. – deepkimo