13.6. Wiązania

Klauzule importu oraz deklaracje pakietów, a także lokalne deklaracje i definicje oraz dziedziczenie, które łącznie nazywa się wiązaniami, mogą wprowadzać w programie różnego rodzaju nazwy. Scala definiuje reguły, zgodnie z którymi jednego rodzaju wiązania są ważniejsze od innych. W poniższych punktach wymienione są różne rodzaje wiązań, począwszy od najważniejszych (w pierwszym punkcie), do najmniej ważnych (w ostatnim punkcie).

W plikach Bindings1.scala, Bindings2.scala, Bindings3.scala oraz Bindings4.scala zdefiniowane są trzy pakiety: a, b i c. Zarówno w pakiecie a, jaki i w b, zdefiniowany jest obiekt o nazwie A. W plikach Bindings2.scala, Bindings3.scala oraz Bindings4.scala znajdują się odwołania do nazwy A niepoprzedzonej nazwą pakietu.

Plik Bindings1.scala:
package a
object A { override def toString = "a.A" }
Plik Bindings2.scala:
package b
import a._ 
object A { override def toString = "b.A" } 
object Bindings2 extends App {
  println(A) 
}
Plik Bindings3.scala:
package c
import a.A, b._ 
object Bindings3 extends App {
  println(A) 
}
Plik Bindings4.scala:
package b {
  object Bindings4 extends App {
    import a._ 
    println(A) 
  }
}

Odwołanie do nazwy A w obiekcie Bindings2, z pliku Bindings2.scala (wiersz ), jest odwołaniem do obiektu b.A, a nie do obiektu a.A, gdyż ten pierwszy jest zdefiniowany lokalnie (w wierszu ), a nazwa tego drugiego jest importowana z innego pakietu przez klauzulę importu (w wierszu ), wobec czego ma mniejsze znaczenie od lokalnej definicji. Fakt odwołania do obiektu b.A potwierdza wynik wykonania poniższego polecenia.

$ scala b.Bindings2
b.A

W pliku Bindings3.scala klauzula importu z wiersza odnosi się do obiektu a.A w sposób bezpośredni, a do obiektu b.A poprzez znak podkreślenia i wobec tego odwołanie do nazwy A w obiekcie Bindings3 (wiersz ) odnosi się do obiektu a.A, a nie b.A. Potwierdza to wynik poniższego polecenia.

$ scala c.Bindings3
a.A

W pliku Bindings4.scala klauzula importu w obiekcie Bindings4 (wiersz ) powoduje, że odwołanie do obiektu A z wiersza nie dotyczy obiektu b.A, zdefiniowanego w tym samym pakiecie, choć w innym pliku źródłowym, ale dotyczy obiektu a.A. Potwierdza to wynik poniższego polecenia.

$ scala b.Bindings4
a.A

Wiązania mają zakresy, w których są ważne. W pliku Bindings5.scala znajduje się definicja klasy Bindings5, w której wartość x jest zdefiniowana w osobnym bloku. Odwołanie do wartości x w pierwszej z metod println (wiersz ) znajduje się w tym samym bloku, ale odwołanie do niej w drugiej z metod println (wiersz ) jest poza tym blokiem i w związku z tym nazwa x jest poza zakresem.

Plik Bindings5.scala:
class Bindings5 {
  {
    val x = 1
    println(x+2) 
  }
  println(x) 
}

W konsekwencji klasa nie kompiluje się.

$ scalac Bindings5.scala
Bindings5.scala:6: error: not found: value x
  println(x)
          ^
one error found

Zakresy wiązań mogą być zagnieżdżane. Wiązanie mające zakres znajdujący się wewnątrz innego zakresu przesłania wiązania mniejszej ważności z tego samego zakresu oraz wiązania o mniejszej lub tej samej ważności z zakresów zewnętrznych.

Plik Bindings6.scala:
object Bindings6 extends App {
  val a = "Hello!" 
  def hello {
    val a = "Hello!!" 
    println(a)
  }
  hello
}

W przykładzie z pliku Bindings6.scala definicja wartości a z wiersza przesłania definicję z wiersza i dlatego metoda println powoduje wyświetlenie wartości zdefiniowanej w wierszu .

$ scala Bindings6
Hello!!

W przykładzie z pliku Bindings7.scala definicja wartości a znajdująca się w metodzie hi przykrywa zaimportowaną wartość a z obiektu Hi, jako że wiązania importowane są mniej ważne od lokalnych definicji.

Plik Bindings7.scala:
object Hi {
  val a = "Hi!"
}
object Bindings7 extends App {
  def hi {
    import Hi.a
    val a = "Hi!!"
    println(a)
  }
  hi
}

O tym fakcie powiadamia nawet ostrzeżenie kompilatora.

$ scalac Bindings7.scala
Bindings7.scala:6: warning: imported `a' is permanently hidden by definition of value a
    import Hi.a
              ^
one warning found

Metoda println wywoływana w metodzie hi powoduje wyświetlenie wartości zdefiniowanej lokalnie, a nie zaimportowanej.

$ scala Bindings7
Hi!!

Wiązanie nie przesłania innego wiązania o tej samej ważności z tego samego zakresu. Definicja drugiej z wartości a z klasy Bindings8, z pliku Bindings8.scala, nie przesłania pierwszej definicji.

Plik Bindings8.scala:
object Bindings8 {
  val a = 1
  val a = 2
}

Próba kompilacji tego pliku kończy się błędem.

$ scalac Bindings8.scala
Bindings8.scala:3: error: a is already defined as value a
  val a = 2
      ^
one error found

Wiązanie z zakresu znajdującego się wewnątrz innego nie przesłania wiązania o większej ważności z zakresu zewnętrznego. Klauzula importu z metody welcome obiektu Bindings9 (wiersz ), którego definicja znajduje się w pliku Bindings9.scala, nie przykrywa ważniejszej definicji z wiersza .

Plik Bindings9.scala:
object Welcome {
  val a = "Welcome!"
}
object Bindings9 {
  val a = "Welcome!!" 
  def welcome {
    import Welcome.a 
    println(a) 
  }
}

Próba kompilacji tego pliku kończy się błędem, gdyż referencja do wartości a z wiersza nie jest jednoznaczna.

$ scalac Bindings9.scala
Bindings9.scala:8: error: reference to a is ambiguous;
it is both defined in object Bindings9 and imported subsequently by
import Welcome.a
    println(a)
            ^
one error found

Próba wczytania w konsoli pliku Bindings8.scala kończy się wyświetleniem komunikatu błędu.

scala> :load Bindings8.scala
Loading Bindings8.scala...
<console>:12: error: a is already defined as value a
         val a = 2
             ^

Jest to spowodowane tym, że w definicji obiektu Bindings8 znajdują się obok siebie dwie definicje wartości a, co jest nieprawidłowe. Jeśli natomiast spróbujemy podwójnie zdefiniować wartość a w konsoli, to uda nam się to. Nowa definicja przykryje starą i konsola nie pokaże komunikatu błędu.

scala> val a = 1
a: Int = 1

scala> val a = 2
a: Int = 2

scala> a
res0: Int = 2

Specyfikacja języka Scala opisuje zasady dotyczące wiązań i zakresów w rozdziale 2.

Język programowania Scala Wydanie 2. Copyright © Grzegorz Balcerek 2016

Licencja Creative Commons

Ten utwór jest dostępny na licencji Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowe.

Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners.