5.10. Klasy wewnątrz klas

Klasy można definiować w ciele innych klas. Przykładem takiej definicji jest klasa Hi2, zdefiniowana wewnątrz klasy Hi1, w pliku Enclosing1.scala.

Plik Enclosing1.scala:
class Hi1 {
  def greet = println("Hi1")
  class Hi2 {
    def greet = println("Hi2")
  }
}

Utworzenie instancji klasy Hi1 nie stanowi problemu.

scala> val hi1 = new Hi1
hi1: Hi1 = Hi1@39680d

scala> hi1.greet
Hi1

Poniższe wyrażenia pokazują jak można utworzyć instancję klasy Hi2.

scala> val hi2 = new hi1.Hi2
hi2: hi1.Hi2 = Hi1$Hi2@76f6a0

scala> hi2.greet
Hi2

Typem utworzonej instancji pokazanym przez konsolę jest hi1.Hi2. Jeśli utworzymy inną instancję klasy Hi1, a następnie inną instancję zawartej w niej klasy Hi2, to obie instancje klasy Hi2 nie będą tego samego typu. Pokazują to następujące wyrażenia.

scala> val hi1b = new Hi1
hi1b: Hi1 = Hi1@44b638
scala> var hi2b = new hi1b.Hi2
hi2b: hi1b.Hi2 = Hi1$Hi2@191b517

scala> hi2b = hi2
<console>:14: error: type mismatch;
 found   : hi1.Hi2
 required: hi1b.Hi2
       hi2b = hi2
              ^

Nie udało się przypisać wartości hi2 do zmiennej hi2b, gdyż ta wartość ma typ hi1b.Hi2, a typem zmiennej jest hi1.Hi2. W nazwach obu typów znajdują się nazwy instancji klasy Hi1. W obu przypadkach są to wartości niezmienne, zdefiniowane przy pomocy słowa kluczowego val. Gdyby były to zmienne, zdefiniowane przy pomocy var, to instancji klasy Hi2 nie udałoby się w ogóle utworzyć. Taką sytuację pokazuje kolejny przykład.

scala> var hi1c = new Hi1
hi1c: Hi1 = Hi1@12a7d73

scala> val hi2c = new hi1c.Hi2
<console>:11: error: stable identifier required, but hi1c found.
       val hi2c = new hi1c.Hi2
                      ^

Kompilator odrzucił definicję, oczekując tak zwanego stabilnego identyfikatora, a zmienne nimi nie są.

Specyfikacja języka Scala opisuje stabilne modyfikatory w punkcie 3.1.

Kolejny przykład pokazuje klasy podobne do poprzednich, ale zawierające dodatkowe metody o nazwie talk.

Plik Enclosing2.scala:
class Hello1 {
  def greet = println("Hello1")
  def talk = { greet; this.greet }
  class Hello2 {
    def greet = println("Hello2")
    def talk = { this.greet; Hello1.this.greet }
  }
}

Do składowych klasy można się odwoływać poprzedzając nazwę składowej przedrostkiem this. Metoda talk z klasy Hello1 wywołuje dwukrotnie metodę greet z tej klasy, przy czym w pierwszym wywołaniu robi to przy pomocy samej nazwy metody, a w drugim za pomocą nazwy poprzedzonej przedrostkiem this. Metoda talk z klasy Hello2 wywołuje metodę greet z klas Hello2 i Hello1. Pierwsze wywołanie, z użyciem przedrostka this, wywołuje metodę greet z klasy Hello2. W celu wywołania metody z klasy Hello1 użyty jest inny przedostek — przedrostek this jest dodatkowo poprzedzony nazwą klasy. Poniższe wyrażenia pokazują rezultaty wywołania obu metod talk.

scala> val hello1 = new Hello1
hello1: Hello1 = Hello1@1815ffc

scala> val hello2 = new hello1.Hello2
hello2: hello1.Hello2 = Hello1$Hello2@1d29a59
scala> hello1.talk
Hello1
Hello1

scala> hello2.talk
Hello2
Hello1

Definicje klas znajdujących się w pliku Enclosing3.scala ilustrują jeszcze inny sposób odwoływania się do składowych klas. Zamiast przedrostka this można użyć przedrostka o nazwie zdefiniowanej samodzielnie. Nazwę takiego przedrostka deklaruje się poprzez umieszczenie na początku ciała klasy (po otwierającym nawiasie klamrowym) parametru, po którym umieszcza się podwójną strzałkę w prawo (znaki => lub znak ). Nazwa tego parametru staje się aliasem przedrostka this. Poprzez taki alias można się odwoływać do instancji klasy.

Plik Enclosing3.scala:
class Welcome1 { welcome1 =>
  def greet = println("Welcome1")
  class Welcome2 { welcome2 ⇒
    def greet = println("Welcome2")
    def talk = {
      welcome1.greet
      welcome2.greet
    }
  }
}

Poniższy przykład pokazuje rezultat wywołania metody talk.

scala> val w1 = new Welcome1
w1: Welcome1 = Welcome1@1bc0606

scala> val w2 = new w1.Welcome2
w2: w1.Welcome2 = Welcome1$Welcome2@1b25825

scala> w2.talk
Welcome1
Welcome2

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.