Der folgende Code gibt einen Kompilierungsfehler aus:

  class Info(val x: String)

  object Info {
    val default = new Info("top")
  }

  case class Data(x: String) {
    import Info.default

    def this() = this(default.x)

  }

Fehler: (11, 23) nicht gefunden: Wert default def this () = this (default.x)

Warum wird das Symbol default trotz des Imports im Konstruktor nicht angezeigt?

Weitere Experimente zeigen, dass es nicht nur wichtig ist. Das Ersetzen der Importzeile durch eine def (oder sogar val) hilft nicht, dennoch bleibt der Fehler bestehen:

def default = Info.default
12
Suma 26 Dez. 2015 im 00:15

3 Antworten

Beste Antwort

Das Scoping funktioniert aufgrund von das Scoping von Selbstkonstruktoraufrufen (wenn Sekundärkonstruktoren den Primärkonstruktor aufrufen):

Die Signatur und der Selbstkonstruktoraufruf einer Konstruktordefinition werden typgeprüft und in dem Bereich ausgewertet, der zum Zeitpunkt der Definition der einschließenden Klasse gültig ist, ergänzt durch alle Typparameter der einschließenden Klasse und durch frühe Definitionen der einschließenden Klasse Vorlage.

Mit anderen Worten, der Gültigkeitsbereich für den Ausdruck default.x ist der Gültigkeitsbereich außerhalb von Data.

Dies ist verwirrend, da es textlich so aussieht, als ob sich dieser Ausdruck innerhalb der Klasse befindet.

Diese Regel hat mit dem lexikalischen Umfang und nicht mit der Bewertungsreihenfolge zu tun.

Sie müssen den Import außerhalb der Klassendefinition verschieben.

Zum Spaß ist das Scoping im folgenden Fall leicht unterbrochen:

object Playroom {
  def default = "playing"
  case class Toy(squeezeMsg: String = default)
  object Toy {
    def default = "srsly"
  }
}

object Test extends App {
  import Playroom._
  println(Toy())
  println(new Toy())
}

Das ist dieses Rätsel. Im Allgemeinen ist es idiomatischer (und robuster), apply -Methoden für den Begleiter zu verwenden.

3
som-snytt 26 Dez. 2015 im 06:50

Dies verhält sich gemäß den Spezifikationen. Der Hauptteil der Klasse ist Teil des sogenannten Primärkonstruktors . Der / die Hilfskonstruktor (en) muss zunächst den zuvor definierten primären oder einen anderen Hilfskonstruktor aufrufen. Angenommen, Sie haben nur ein Auxiliary, da der Aufruf von Primary die erste Anweisung in Ihrem Auxiliary sein muss, bedeutet dies, dass Sie während dieser ersten Anweisung keinen Zugriff auf etwas haben, das in der Primary definiert ist. Nachdem der erste Aufruf abgeschlossen ist, können Sie auf alle im primären definierten Mitglieder und Importe zugreifen.

In Ihrem Beispiel besteht das Problem darin, dass der Import (import Info.default) für die erste Anweisung Ihres Auxiliars nicht sichtbar ist. Wenn Sie es durch def this() = this(Info.default.x) ersetzen, sollte es funktionieren.

Beispiel:

Das Folgende wird gut kompiliert, da der Aufruf von Constant nach dem Aufruf des Primärservers erfolgt.

class G(i: Int) {
   val Constant = 1
   // No argument auxiliary constructor    
   def this() = {
      this(3) // Call to primary 
      println(Constant * 2)
   }
}

Das Folgende wird nicht kompiliert, da der Aufruf von Constant vor dem Aufruf des primären erfolgt, was bedeutet, dass Constant noch nicht erstellt wurde.

class G(i: Int) {
   val Constant = 1
   // No argument auxiliary constructor    
   def this() = {
      this(Constant) // This will cause an error!
   }
}

Lösung:

Haben Sie alle Konstanten, die Sie benötigen, im Begleitobjekt definiert. Das Begleitobjekt wird per Definition zuerst initialisiert, sodass Sie über die Konstruktoren Ihrer Klasse auf alle Mitglieder zugreifen können.

object G {
   val Constant = 1
}

class G(i: Int) {
    // No argument auxiliary constructor    
    def this() = {
       this(G.Constant) // This now works!
    }
 }
3
marios 26 Dez. 2015 im 07:28

In Scala sind alle in der Klasse definierten Felder erst verfügbar, nachdem der primäre Konstruktor aufgerufen wurde. Um diese zu beheben, wird in der Regel ein Companion-Objekt mit apply -Methode verwendet.

0
Alexandr Dorokhin 26 Dez. 2015 im 00:26