16.5. Dostęp chroniony
Oprócz modyfikatora private istnieje też modyfikator protected, który dotyczy definicji znajdujących się wewnątrz klas i pozwala na to, aby modyfikowana definicja mogła być użyta w tej klasie, jej klasach pochodnych oraz obiektach towarzyszących tych klas. Ten modyfikator może być użyty, podobnie jak private, w trzech różnych formach. Jeśli zamienilibyśmy modyfikatory private na protected w przykładach do tej pory pokazanych w tym rozdziale, to działanie zaprezentowanych przykładów nie zmieniłoby się. Przykłady, które nie kompilowały się z modyfikatorem private, nie kompilowałyby się nadal z modyfikatorem protected. Przykłady, które działały z modyfikatorem private, działałyby również z modyfikatorem protected. Z tego względu nie będziemy powtarzać tutaj powyższych przykładów zmieniając jedynie modyfikator na protected. Czytelnik jeśli chce, może tego spróbować samodzielnie. Do tej pory jednak przykłady z tego rozdziału nie obejmowały dziedziczenia. Teraz przejdziemy do przykładów, które zawierają elementy dziedziczenia. W tych przykładach będzie można zaobserwować różnice między modyfikatorami private i protected. Przyjrzyjmy się zatem dziedziczeniu i porównajmy działanie tych modyfikatorów.
Plik Protected1.scala: class Greeting(greeting: String) { private def speak = println(greeting) private[this] def speak2 = println(greeting + " " + greeting) } object Hello extends Greeting("Hello") { def talk = speak def talk2 = speak2 }
W przykładzie z pliku Protected1.scala obiekt Hello nie kompiluje się, gdyż próbuje uzyskać dostęp do prywatnych składowych swojej klasy nadrzędnej. W tym przykładzie ani modyfikator private, ani private[this] nie pozwala na dostęp do składowych z klasy podrzędnej.
$ scalac Protected1.scala Protected1.scala:6: error: not found: value speak def talk = speak ^ Protected1.scala:7: error: not found: value speak2 def talk2 = speak2 ^ two errors found
Wymiana w klasie Greeting modyfikatorów private na protected zmienia sytuację. Poprawiony kod znajduje się w pliku Protected2.scala.
Plik Protected2.scala: class Greeting(greeting: String) { protected def speak = println(greeting) protected[this] def speak2 = println(greeting + " " + greeting) } object Hello extends Greeting("Hello") { def talk = speak def talk2 = speak2 }
Teraz kod kompiluje się i może być wykonany. Obiekt Hello dziedziczy metody z klasy Greeting i ma do nich dostęp.
scala> Hello.talk Hello scala> Hello.talk2 Hello Hello
Przykłady z plików Protected3.scala i Protected4.scala ilustrują różnicę między między działaniem modyfikatora protected, a protected[this], czyli między dostępem chronionym, a chronionym w ramach obiektu.
Plik Protected3.scala: class Greeting(greeting: String) { protected def speak = println(greeting) } class Hello(name: String) extends Greeting("Hello "+name) { def talkTo(other: Hello) { this.speak other.speak } }
W przykładzie z pliku Protected3.scala, w klasie Greeting jest zdefiniowana chroniona metoda speak. Dziedzicząca po Greeting klasa Hello wywołuje, w metodzie talkTo, metodę speak na dwóch obiektach, najpierw na swoim własnym obiekcie, a później na obiekcie przekazanym w parametrze other.
scala> val peter = new Hello("Peter") peter: Hello = Hello@c4ddae scala> val john = new Hello("John") john: Hello = Hello@f6d34c scala> peter talkTo john Hello Peter Hello John
Wywołanie metody talkTo udaje się. Metody speak w obu obiektach są dostępne. Sytuacja zmieni się, gdy klasie Greeting modyfikator protected zmieni się na protected[this]. Zmodyfikowana definicja znajduje się w pliku Protected4.scala.
Plik Protected4.scala: class Greeting(greeting: String) { protected[this] def speak = println(greeting) } class Hello(name: String) extends Greeting("Hello "+name) { def talkTo(other: Hello) { this.speak other.speak } }
Teraz dostęp do metody speak jest bardziej ograniczony. Kompilator nie pozwala na dostęp do metody speak obiektu other, przekazanego w parametrze metody talkTo (wiersz ). Natomiast wywołanie this.speak (wiersz ) nie powoduje błędu kompilacji.
$ scalac Protected4.scala Protected4.scala:7: error: value speak is not a member of Hello other.speak ^ one error found