2013-07-07 7 views
8

Ho una classe che ha un metodo il cui identificatore di accesso di default è pubblico. Ora, vorrei estendere questa classe in una sottoclasse e voglio sovrascrivere questo metodo per avere l'identificatore di accesso "privato". Quando si compila questo codice, sto ottenendo un errore di compilazione:perché non possiamo assegnare privilegi più deboli nella sottoclasse

"attempting to assign weaker access privileges".

Qualcuno potrebbe spiegare a me ciò che è sbagliato con l'assegnazione di privilegi più deboli in una sottoclasse?

Ecco il codice che ha causato l'errore di compilazione:

class Superclass 
{ 
    void foo() 
    { 
     System.out.println("Superclass.foo"); 
    } 
} 

class Subclass extends Superclass 
{ 
    private void foo() 
    { 
     System.out.println("Subclass.foo"); 
    } 
} 
+3

Interessante domanda! Questo stesso caso * è * permesso in C++. – quamrana

+1

"Ho una classe che ha un metodo il cui identificatore di accesso è di default pubblico". In realtà l'identificatore di accesso predefinito non è pubblico. Se non hai menzionato alcun specificatore di accesso specifico, allora è "predefinito" :). – pikrut

+0

@quamrana E cosa succede in C++ quando si accede ad un oggetto attraverso una variabile tipizzata da superclasse? Il compilatore sicuramente lo lascia passare ... errore durante il runtime? –

risposta

25

La risposta breve è che non è consentito perché si spezzerebbe tipo sostituibilità; vedere anche lo Liskov Substititution Principle (LSP).

Il punto è che il polimorfismo in Java (e altri linguaggi di programmazione) si basa su di voi essere in grado di trattare un'istanza di una sottoclasse come se fosse un'istanza della superclasse. Ma se il metodo è limitato nella sottoclasse, si scopre che il compilatore non riesce a capire se le regole di accesso consentono un metodo da chiamare ...

Per esempio, lascia supporre che il codice di esempio era legale:

// Assume this code is in some other class ... 

SuperClass s1 = new SuperClass(); 

s1.foo();       // OK! 

SuperClass s2 = new Subclass(); 

s2.foo();       // What happens now? 

SuperClass s3 = OtherClass.someMethod(); 

s3.foo();       // What happens now? 

Se si basa la decisione se s2.foo() è consentito il tipo dichiarato di s2, poi si consente una chiamata a un metodo di private dal di fuori del confine astrazione della Subclass.

Se si basa la decisione sul tipo effettivo dell'oggetto a cui fa riferimento s2, non è possibile eseguire il controllo di accesso in modo statico. Il caso s3 lo rende ancora più chiaro. Il compilatore non ha assolutamente modo di sapere quale sarà il tipo effettivo dell'oggetto restituito da someMethod.

I controlli di accesso che potrebbero causare eccezioni di runtime sarebbero una delle principali fonti di bug nell'applicazione Java. La restrizione della lingua in discussione qui evita questo brutto problema.

+1

+1 Se corretto, per capire la tua risposta avresti bisogno di più conoscenze di quelle implicite nella domanda. ;) –

+1

@PeterLawrey - Scusa ... ho dovuto aiutare ad affrontare un pesce morto. Letteralmente. –

+0

Nessun problema, ha fatto +1 su di esso in qualsiasi modo. ;) Ho a che fare con il jet lag, sono le 2:55 del mattino. Hai bisogno di tornare a dormire. –

7

Non è possibile limitare l'accesso perché è già stato concesso un maggiore accesso nella super classe. per esempio.

SuperClass sc = new SubClass(); 
sc.foo(); // is package local, not private. 

L'accesso di sc è determinato dal tipo di riferimento non è quello che fa riferimento, perché è impossibile che il compilatore di conoscere in tutti i casi che tipo l'oggetto è in fase di esecuzione sc. Affinché ciò sia un presupposto sicuro, la sottoclasse deve onorare il contratto dato dal genitore o non è una sottoclasse valida. Questo non è diverso dal genitore dicendo che un metodo è implementato ma la sottoclasse che dice di non essere (o non accessibile)

Si potrebbe aggirare questo dicendo che si può accedere al metodo della sottoclasse solo tramite il genitore, non direttamente. Il problema con questo è che non sai quando un genitore potrebbe aggiungere un metodo e quando fai un metodo private lo fai perché vuoi che sia privato e non accessibile in un altro modo.

BTW È ancora possibile accedere a un metodo privato tramite la riflessione che ha l'effetto collaterale di causare tutti i tipi di problemi per la JVM. per esempio. deve mantenere i metodi privati ​​anche se potrebbe determinare che non è possibile chiamarlo normalmente.

In breve, si desidera codice che significa ciò che dice e non ha una doppia personalità. È un pacchetto locale o privato non è una specie di intermedio, ma non proprio. Questo non è un problema nell'altro senso. Ad esempio, se la sottoclasse è pubblica. Significa solo che la sottoclasse può essere utilizzata in più posti rispetto al genitore, proprio come può implementare più metodi.

+3

Sì, ma perché deve essere (è ciò che sta chiedendo)? – Bohemian

0

A parte gli ovvi problemi con l'utilizzo di tali costruzioni (come indicato da Peter Lawrey nella sua risposta), leggi anche la teoria alla base: LSP, che significa che devi essere in grado di sostituire il tipo principale con il suo sottoclasse.

+0

+1 LSP è il motivo. Buon collegamento – Bohemian

1

costrizione il modificatore di accesso di un metodo di classe Super è un override non valida perché è la rottura del contratto di super-classe ed invalida il principio di sostituzione vale a dire un oggetto sottoclasse IS-Un oggetto super-classe pure.

public void doSomething(SuperClass sc) { 
    sc.publicMethodInSuperClass(); 
} 

doSomething(new SubClass()); // would break 

Se questo è stato permesso, il codice del client di cui sopra si spezzerebbe, perché il vostro SubClass non ha che il metodo pubblico.

Riferimento:
Liskov substitution principle

5

Se questo è stato permesso, ci sarebbe una backdoor attraverso il quale si potrebbe chiamare i metodi che non dovrebbero essere accessibili.

permette di dire che questo è permesso

class Super { 
    public void method() { 
     System.out.println("Super"); 
    } 
} 


class Sub extends Super { 
    // This is not allowed, but suppose it was allowed 
    protected void method() { 
     System.out.println("Sub"); 
    } 
} 

// In another class, in another package: 
Super obj = new Sub(); 
obj.method(); 

obj.method sarebbe possibile, perché è method()public in classe Super. Ma non dovrebbe essere permesso, perché obj si riferisce veramente a un'istanza di Sub, e in quella classe, il metodo è protetto!

Per limitare una chiamata a un metodo in classe Sub che non dovrebbe essere accessibile dall'esterno, questa restrizione viene inserita.

0

Penso che la risposta breve sia che gli autori del compilatore hanno impostato le regole per funzionare in questo modo. LSP non ha nulla a che fare con il problema in questione.

L'unica ragione che posso pensare di avere questa restrizione è che quando una sottoclasse deriva da un'interfaccia, come programmatore client, ci si aspetta di essere in grado di chiamare tutti i metodi accessibili dell'interfaccia da un riferimento al derivato classe.

Si supponga di poter scrivere il codice mostrato dall'OP. Se hai un riferimento alla classe derivata dovresti essere in grado di chiamare qualsiasi membro pubblico della classe derivata (anche se in questo caso non ce ne sono). Tuttavia, passare il riferimento come parametro a un metodo che prende un riferimento alla classe base e il metodo prevede di chiamare qualsiasi metodo public o package, che è foo. Questo è l'LSP che gli altri contributori stanno cercando!

C++ esempio:

class Superclass{ 
public: 
virtual void foo(){ cout << "Superclass.foo" << endl; } 
}; 

class Subclass: public Superclass{ 
virtual void foo(){ cout << "Subclass.foo" << endl; } 
}; 

int main(){ 
Superclass s1; 
s1.foo() // Prints Superclass.foo 

Subclass s2; 
// s2.foo(); // Error, would not compile 

Superclass& s1a=s2; // Reference to Superclass 'pointing' to Subclass 

s1a.foo(); // Compiles OK, Prints Subclass.foo() 
} 
+0

* "L'unica ragione per cui posso pensare di avere questa restrizione è che quando una sottoclasse deriva da un'interfaccia, come programmatore client, ci si aspetta di essere in grado di chiamare tutti i metodi accessibili dell'interfaccia da un riferimento alla classe derivata . "* - Questo è fondamentalmente LSP. Perché se il programmatore client non può farlo, allora la sottoclasse non può essere sostituita per la classe genitore! –

+0

No, non è così. LSP si riferisce alla prospettiva del cliente in cui conoscono solo la classe base e si aspettano che la classe base funzioni in un certo modo. Quando fornisci una classe derivata al client perché la tua lingua lo consente, la classe derivata dovrebbe comportarsi allo stesso modo, in modo tale che la funzione client funzioni ancora. – quamrana

+0

Penso che tu stia interpretando l'LSP in modo troppo ristretto. La definizione di Liskov & Wing è questa: * "Sottotipo Requisito: Sia φ (x) una proprietà dimostrabile sugli oggetti x di tipo T. Quindi φ (y) dovrebbe essere vero per gli oggetti y di tipo S dove S è un sottotipo di T . "*. In questo caso, φ sta avendo un metodo accessibile 'pippo'. Questa è una proprietà di 'SuperClass' su cui i client dipendono, e quindi deve anche essere una proprietà di' SubClass' ... oppure un'istanza di quest'ultimo non è sostituibile per un'istanza del primo. Vedi https://en.wikipedia.org/wiki/Liskov_substitution_principle –

0

Nel metodo dispatch dinamico, chiamare il metodo override viene risolto in fase di esecuzione, piuttosto che compilare il tempo. Si basa sull'oggetto a cui si fa riferimento al momento della chiamata ...

Ora supponiamo debole privilegi di accesso è stato consentito e scriviamo la seguente dichiarazione nel codice qualche altra classe:

Superclass ref=new Subclass(); 
ref.foo() 

Ora durante il runtime, quando java incontra la dichiarazione ref.foo(), si dovrà chiamare foo() di Subclass ... ma il metodo foo() di sottoclasse è dichiarato come privato nel codice e il privato non può essere chiamato al di fuori della sua classe..so ora c'è un conflitto e risulterebbe in un'eccezione di runtime ...

Problemi correlati