2013-04-05 14 views
5

zdarzyło mi się dowiedzieć, że nie wolno mieć abstrakcyjne pól prywatnych w cechę, że jestAbstrakt prywatne pola cechy Scala

trait A1 { 
    //private val a: Int   // Not allowed 
    protected val b: Int   // OK 
} 

i wydaje wszelkie prawa robić czegoś takiego do klasy abstrakcyjnej jeśli prywatne pola są parametry konstruktora, czyli

abstract class A2 (private val i: Int) // OK 

więc myślę, że to cecha nie posiada parametrów konstruktora, więc nie ma sposób je zainicjować, dlatego nie abstrakcyjny prywatny FIE lds są dozwolone.

Jeśli są "chronione", wówczas podklasa może zainicjować je za pomocą wstępnie zainicjowanych pól . To podejście umożliwia wyświetlenie tych pól podklasą.

Co zrobić, jeśli chcę je zainicjować i ukryć później, , jak w poniższym przykładzie?

object holding { 
    trait trick { 
     protected val seed: Int      // Can't be private 
     final def magic: Int = seed + 123 
    } 

    trait new_trick extends trick { 
     def new_magic: Int = magic + 456 
     def the_seed: Int = seed     // [1] 
    } 

    def play: new_trick = new { val seed = 1 } with new_trick 

    def show_seed(t: new_trick): Int = t.the_seed // [2] 
} 

że nie ktoś chce się być w stanie dostrzec nasion, to [2] (a więc i [1]), nie powinny być dozwolone. Czy istnieje sposób, aby to zrobić?


Jak @Randall i @ pagoda_5b podkreśliło, moje pytanie nie ma większego sens. Ale na szczęście @ Régis i @ axel22 zamienili to w kolejne interesujące pytanie i dostarczyli wzór do jego rozwiązania.

+1

Jak ważne jest posiadanie czegoś prywatnego i niezaimplementowanego? Jest to sprzeczność, ponieważ nie jest ona dziedziczona i nigdy nie może zostać zaimplementowana, a zatem uniemożliwiłaby utworzenie dowolnego podtypu cechy posiadającej takiego członka. –

+1

Nie jestem pewien, czy podążam za tobą. Jeśli potrzebujesz podklas do zdefiniowania materiału siewnego (przesłonięcie jego definicji), nie ma sensu, aby było ono prywatne. To powiedziawszy, podklasa, która definiuje implementację elementu źródłowego, zawsze będzie w stanie pokazać go publicznie dostępnemu. Nie rozumiem potrzeby stojącej za tym wyborem. –

+0

Myślę, że sprowadza się to do tego, że bezpośrednia sub-cecha definiuje wartość "nasiona", a dalsze podkategorie (i kod zewnętrzny) nie mogą uzyskać dostępu do wartości. Coś w rodzaju wartości chronionej (traktuj jako wartość chronioną dla podtekstów i traktuj jako prywatne dla wszystkiego, co lese). Co do znaczenia, myślę, że chodzi o to, aby w pełni naśladować, co można zrobić za pomocą parametrów (w klasach), jak pokazano w jego przykładzie. –

Odpowiedz

8

Prostym sposobem na zachowanie wartości val private przy jednoczesnym umożliwieniu initfikacji podtekstów byłoby zdefiniowanie go jako prywatnego, ale zainicjowanie go wartością zwróconą przez inną chronioną metodę. Następnie podkategorie mogą definiować tę chronioną metodę, aby zmienić wartość początkową, ale nie mogą uzyskać dostępu do samej wartości. Tak by to zmienić:

trait A { 
    protected val foo: Bar 
} 

do:

trait A { 
    private val foo: Bar = initFoo 
    protected def initFoo: Bar 
} 

Teraz tylko trait A mogą uzyskać dostęp do Val foo. Sub-cechy można ustawić wartość początkową foo przez definint initFoo, ale nie może uzyskać dostępu foo się:

trait B extends A { 
    protected def initFoo: Bar = ??? 
} 

Oczywiście sama initFoo jest nadal dostępny dla podsektorów cech. Często nie jest to problemem, jeśli initFoo tworzy nową instancję za każdym razem (innymi słowy, jest to fabryka), , ponieważ być może interesuje nas tylko uczynienie tej instancji prywatną dla A, nie martwiąc się o to, że podobiekty będą w stanie twórz nowe instancje Bar (niezależnie od tego, czy nowe instancje są równe foo zgodnie z ich metodą equals).

Ale jeśli jest to problemem (i to na pewno jest w twoim przypadku jak seed jest fo typu Int a więc to, co chcesz, aby ukryć to wartość, a nie tylko odniesienia), możemy wykorzystać dodatkowy trik, aby umożliwić podtyki do zdefiniowania initFoo, ale uniemożliwiają im (i ich podobieństwom) możliwość wywoływania go. Ta sztuczka jest, powiedzmy sobie, dość okropna na taką prostą potrzebę, ale ilustruje ładny wzór zaawansowanej kontroli dostępu. Kredyty należą do autorów standardowej biblioteki pomysłu (patrz http://www.scala-lang.org/api/current/index.html#scala.concurrent.CanAwait).

trait A { 
    // A "permit" to call fooInit. Only this instance can instantiate InitA 
    abstract class InitA private[this]() 
    // Unique "permit" 
    private implicit def initA: InitA = null 

    private def foo: Int = fooInit 
    protected def fooInit(implicit init: InitA): Int 
} 

trait B extends A { 
    protected def fooInit(implicit init: InitA): Int = 123 
} 

Teraz, jeśli B próbuje wywołać initFoo, kompilator będzie narzekać, że nie może znaleźć niejawna typu InitA (unikalna takie wystąpienie jest A.initA i jest tylko dostepne w A).

Jak już powiedziałem, jest to trochę okropne, a rozwiązanie prywatne pakietu podane przez axel22 jest z pewnością znacznie prostszą alternatywą (chociaż nie przeszkodzi to nikomu w zdefiniowaniu ich podtreści w tym samym pakiecie co twój, pokonując w ten sposób ograniczenie dostępu).

+0

"Teraz, jeśli B próbuje wywołać foo, kompilator będzie ...", miałeś na myśli "jeśli B próbuje wywołać fooInit" ? – cfchou

+0

Tak, rzeczywiście, to właśnie miałem na myśli, dziękuję za poprawkę. –

6

Najlepsze, co możesz zrobić, to zadeklarować prywatność tych pakietów - private[package_name]. Umożliwiłoby to rozszerzenie i zdefiniowanie cechy w obrębie tego samego pakietu, w którym wykonywana jest implementacja, ale uniemożliwienie klientom używania jej z innych pakietów.