In Java, perché i membri protetti sono stati resi accessibili alle classi dello stesso pacchetto?

14

Dalla documentazione ...

ufficiale
Modifier    Class   Package Subclass    World 
public      Y       Y       Y           Y 
protected   Y       Y       Y           N 
no modifier Y       Y       N           N 
private     Y       N       N           N 

Il fatto è che non ricordo di avere un caso d'uso in cui avevo bisogno di accedere ai membri protetti da una classe all'interno dello stesso pacchetto.

Quali sono stati i motivi alla base di questa implementazione?

Modifica: per chiarire, sto cercando un caso d'uso specifico in cui sia una sottoclasse che una classe all'interno dello stesso pacchetto devono accedere a un campo o metodo protetto.

package some.package;
public class A {
 protected void protectedMethod(){
  // do something
 }
}

package another.package;
public class B extends A{
 public void someMethod(){
  // accessible because B is a subclass of A
  protectedMethod();
 }
} 

package some.package;
public class C {
 public void anotherMethod(){
  // accessible because C is in the same package as A
  protectedMehtod();
 }
}
    
posta jramoyo 22.07.2013 - 11:55
fonte

7 risposte

4

looking for a specific use case where both a subclass and a class within the same package needs to access a protected field or method...

Per me, un caso d'uso di questo tipo è piuttosto generale che specifico e deriva dalle mie preferenze per:

  1. Inizia con il più rigoroso modificatore di accesso possibile, ricorrendo a uno o più deboli solo più tardi, se necessario.
  2. I test unitari si trovano nello stesso pacchetto del codice testato.

Da sopra, posso iniziare a progettare i miei oggetti con modificatori di accesso predefiniti (vorrei iniziare con private ma ciò complicherebbe il test delle unità):

public class Example {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        void doSomething() {} // default access
    }
    static class Unit2 {
        void doSomething() {} // default access
    }

    static class UnitTest {
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

Nota laterale in snippet sopra, Unit1 , Unit2 e UnitTest sono nidificato in Example per semplicità di presentazione, ma in un progetto reale, probabilmente avrei queste classi in file separati (e UnitTest anche in una directory separata ).

Quindi, quando sorge una necessità, indebolire il controllo degli accessi da predefinito a protected :

public class ExampleEvolved {
    public static void main(String [] args) {
        new UnitTest().testDoSomething(new Unit1(), new Unit2());
    }

    static class Unit1 {
        protected void doSomething() {} // made protected
    }
    static class Unit2 {
        protected void doSomething() {} // made protected
    }

    static class UnitTest {
        // ---> no changes needed although UnitTest doesn't subclass
        // ...and, hey, if I'd have to subclass... which one of Unit1, Unit2?
        void testDoSomething(Unit1 unit1, Unit2 unit2) {
            unit1.doSomething();
            unit2.doSomething();
        }
    }
}

Vedete, posso mantenere il codice del test unitario in ExampleEvolved invariato a causa dei metodi protetti accessibili dallo stesso pacchetto, anche se l'accesso all'oggetto è non una sottoclasse .

Meno modifiche necessarie = > modifica più sicura; dopotutto ho modificato solo i modificatori di accesso e non ho modificato i metodi Unit1.doSomething() e Unit2.doSomething() , quindi è normale aspettarsi che il codice di prova dell'unità continui a funzionare senza modifiche.

    
risposta data 22.07.2013 - 18:40
fonte
5

Direi che ha due parti:

  1. Il default, "pacchetto", l'accesso è utile in un'ampia gamma di casi, perché le classi non sono sempre una buona unità di incapsulamento. Vari oggetti compositi in cui alcuni oggetti fungono da raccolta di altri oggetti, ma gli elementi non devono essere modificabili pubblicamente, poiché esistono alcuni invarianti nell'intera raccolta, pertanto la raccolta deve disporre di un accesso elevato agli elementi. C ++ ha amici, Java ha accesso al pacchetto.
  2. Ora l'ambito di accesso al "pacchetto" è sostanzialmente indipendente dall'ambito "protetto" (sottoclasse). Quindi avresti bisogno di ulteriori specificatori di accesso per il solo pacchetto, solo sottoclassi e pacchetto e sottoclassi. L'ambito del "pacchetto" è più limitato in quanto l'insieme di classi in un pacchetto è generalmente definito mentre una sottoclasse può apparire ovunque. Per semplificare le cose, Java include semplicemente l'accesso al pacchetto nell'accesso protetto e non ha uno specificatore aggiuntivo per sottoclassi-ma-non-pacchetto. Anche se dovresti quasi sempre pensare a protected esattamente come questo.
risposta data 22.07.2013 - 13:29
fonte
5

IMHO, questa è stata una pessima decisione di progettazione in Java.

Solo speculazioni, ma penso che volessero che i livelli di accesso fossero in una progressione rigorosa: privato - "pacchetto" - protetto - pubblico. Non volevano avere una gerarchia, dove alcuni campi sarebbero disponibili per il pacchetto ma non per le sottoclassi, alcuni disponibili per le sottoclassi ma non per il pacchetto, e alcuni entrambi.

Tuttavia, a mio modesto parere, avrebbero dovuto passare dall'altra parte: ha detto che la protezione è visibile solo alla classe e alle sottoclassi e che quel pacchetto è visibile alla classe, alle sottoclassi e al pacchetto.

Spesso ho dati in una classe che deve essere accessibile alle sottoclassi, ma non al resto del pacchetto. Ho difficoltà a pensare a un caso in cui era vero il contrario. Ho avuto alcune rare occasioni in cui ho avuto un insieme di classi correlate che hanno bisogno di condividere dati, ma che dovrebbero tenere al sicuro i dati al di fuori del pacchetto. Va bene, mettili in un pacchetto, ecc. Ma non ho mai avuto un caso in cui voglio che il pacchetto condivida i dati, ma voglio tenerlo lontano dalle sottoclassi. Ok, posso immaginare la situazione in arrivo. Suppongo che questo pacchetto sia parte di una libreria che potrebbe essere estesa da classi di cui non so nulla, per lo stesso motivo per cui renderei i dati in una classe privata. Ma è molto più comune voler rendere i dati disponibili solo alla classe e ai suoi figli.

Ci sono molte cose che tengo private tra me e i miei figli e non condividiamo con i vicini. C'è molto poco che tengo privato tra me e i vicini e non condivido con i miei figli. : -)

    
risposta data 14.03.2016 - 05:46
fonte
0

Un buon esempio che viene subito in mente sono le classi di utilità che vengono utilizzate molto nel pacchetto ma non si desidera l'accesso pubblico (dietro le quinte-caricamento immagine-da-disco, creazione-creazione / distruzione classi, ecc.) invece di creare tutto [equivalente di Java friend access modifier or idiom in C++ ] di ogni altra classe, tutto è automaticamente disponibile.

    
risposta data 22.07.2013 - 12:37
fonte
0

I casi d'uso per il modificatore di accesso protetto / pacchetto sono simili a quelli per i amici modificatori di accesso in C ++.

Un caso d'uso è quando si implementa il schema Memento .

L'oggetto ricordo deve accedere allo stato interno di un oggetto per mantenerlo, in modo da fungere da punto di controllo per le operazioni di annullamento.

Dichiarare la classe nello stesso pacchetto è uno dei possibili modi per ottenere il pattern di Memento, dal momento che Java non ha alcun "amico" modificatore di accesso.

    
risposta data 22.07.2013 - 12:44
fonte
-1

simmetria?
È raro avere bisogno di tale accesso, motivo per cui l'accesso predefinito è usato raramente. Ma a volte i framework lo richiedono per il codice generato, dove le classi wrapper sono inserite nello stesso pacchetto che interagisce direttamente con le classi, andando ai membri invece degli accessor per motivi di prestazioni. Pensa a GWT.

    
risposta data 22.07.2013 - 12:12
fonte
-1

La gerarchia di incapsulamento di Java è ben definita:

Classe - > Pacchetto - > Eredità

"Protetto" è solo una forma di privacy più debole rispetto al pacchetto predefinito, come deciso dai progettisti Java. L'accesso agli elementi predefiniti del pacchetto è limitato a un sottoinsieme delle entità cui è consentito accedere agli elementi protetti.

Ha molto senso da un punto di vista matematico e di implementazione per avere i gruppi di entità a cui è consentito accedere alle cose da annidare. (Non è possibile nidificare il set di accesso al pacchetto all'interno del set di accesso protetto perché le classi possono ereditare da altri pacchetti).

Ha senso dal punto di vista concettuale avere qualcosa in java.util che sia "più amichevole" con un'altra classe nel pacchetto rispetto a qualcosa in com.example.foo.bar che la sottoclasse. Nel primo caso è probabile che le classi siano scritte dallo stesso autore, o almeno dai programmatori della stessa organizzazione.

    
risposta data 22.07.2013 - 17:40
fonte