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).
- Definicje i deklaracje, które są lokalne, odziedziczone lub dostępne przez klauzulę pakietu w tej samej jednostce kompilacji (w tym samym pliku źródłowym).
- Klauzule importu zawierające odwołania do konkretnych nazw.
- Klauzule importu zawierające odwołania wieloznaczne (za pomocą znaku podkreślenia).
- Definicje dostępne przez klauzulę pakietu z innej jednostki kompilacji.
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. |
Plik Bindings1.scala:
package a
object A { override def toString = "a.A" }


