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


14

Dalla documentazione ufficiale ...

Mondo di sottoclasse pacchetto classe modificatore 
AAAA pubblico 
YYYN protetto 
nessun modificatore YYNN 
YNNN privato 

Il fatto è che non ricordo di avere un caso d'uso in cui avevo bisogno di accedere a 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();
 }
}

Risposte:


4

alla ricerca di 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 ...

Bene per me, un tale caso d'uso è 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 in seguito, se ritenuto necessario.
  2. I test unitari devono risiedere nello stesso pacchetto del codice testato.

Da sopra, posso iniziare a progettare i miei oggetti con modificatori di accesso predefiniti (vorrei iniziare privatema ciò complicherebbe il test unitario):

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 a margine nello snippet sopra Unit1, Unit2e UnitTestsono nidificati all'interno Exampleper semplicità di presentazione, ma in un progetto reale, probabilmente avrei queste classi in file separati (e UnitTestpersino in una directory separata ).

Quindi, quando si presenta una necessità, indebolirei il controllo degli accessi dal default 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 ExampleEvolvedinvariato il codice unit test a causa dell'accessibilità dei metodi protetti dallo stesso pacchetto, anche se l'accesso all'oggetto non è una sottoclasse .

Meno cambiamenti necessari => modifica più sicura; dopo tutto ho cambiato solo i modificatori di accesso e non ho modificato quali metodi Unit1.doSomething()e Unit2.doSomething()cosa fare, quindi è naturale aspettarsi che il codice unit test continui a funzionare senza modifiche.


5

Direi che questo ha due parti:

  1. L'impostazione predefinita, "pacchetto", è utile in molti casi, poiché le classi non sono sempre una buona unità di incapsulamento. Vari oggetti compositi in cui alcuni oggetti agiscono come raccolta di altri oggetti, ma gli oggetti non devono essere modificabili pubblicamente, poiché ci sono alcune invarianti nell'intera raccolta, quindi la raccolta deve avere un accesso elevato agli oggetti. C ++ ha amici, Java ha accesso al pacchetto.
  2. Ora l'ambito di accesso "pacchetto" è sostanzialmente indipendente dall'ambito "protetto" (sottoclasse). Quindi avresti bisogno di ulteriori identificatori di accesso solo per pacchetto, solo sottoclassi e pacchetto e sottoclasse. L'ambito del "pacchetto" è più limitato poiché un insieme di classi in un pacchetto è generalmente definito mentre una sottoclasse può apparire ovunque. Quindi, per semplificare le cose, Java include semplicemente l'accesso al pacchetto nell'accesso protetto e non ha un identificatore aggiuntivo per le sottoclassi ma non il pacchetto. Anche se dovresti quasi sempre pensare protectedesattamente a quello.

Non sarebbe più semplice se protectedfosse solo una sottoclasse? Onestamente, per molto tempo, ho avuto l'impressione che questo sia il comportamento
jramoyo,

@jramoyo: No, perché avresti comunque bisogno di rendere il comportamento combinato disponibile in qualche modo, il che significherebbe un altro identificatore.
Jan Hudec,

6
@jramoyo: in C # protectedè solo la classe e la sottoclasse ed internalè a livello di libreria / pacchetto. Ha anche protected internalqual è l'equivalente di Java protected.
Bobson,

@Bobson - grazie, l'implementazione di C # sembra una scelta migliore
jramoyo

5

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

Ho solo speculato, ma penso che volessero che i livelli di accesso fossero in stretta progressione: privato - "pacchetto" - protetto - pubblico. Non volevano avere una gerarchia, in cui alcuni campi sarebbero stati disponibili per il pacchetto ma non sottoclassi, alcuni disponibili per sottoclassi ma non il pacchetto e altri entrambi.

Tuttavia, secondo la mia modesta opinione, avrebbero dovuto andare dall'altra parte: detto che il protetto è visibile solo alla classe e alle sottoclassi, e 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. Mi viene difficile pensare a un caso in cui è vero il contrario. Ho avuto alcune rare occasioni in cui ho avuto un gruppo di classi correlate che devono condividere dati ma che dovrebbero tenerli al sicuro dall'esterno del pacchetto. Va bene, inseriscili in un pacchetto, ecc. Ma non ho mai avuto un caso in cui volevo che il pacchetto condividesse dati, ma voglio tenerlo lontano dalle sottoclassi. Ok, posso immaginare la situazione che si presenta. Suppongo che questo pacchetto faccia 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. :-)


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 (classi dietro le quinte di caricamento delle immagini da disco, gestione della creazione / distruzione, ecc. .) invece di rendere tutto [l'equivalente Java friend access modifier or idiomin C++] di ogni altra classe, tutto è automaticamente disponibile.


1
Le classi di utilità sono più un esempio di classi che sono interne nel loro insieme piuttosto che i loro membri.
Jan Hudec,

0

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

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

L'oggetto memento deve accedere allo stato interno di un oggetto, trattenerlo, al fine di fungere da punto di controllo per le operazioni di annullamento.

Dichiarare la classe nello stesso pacchetto è uno dei modi possibili per ottenere il modello Memento, poiché Java non ha modificatore di accesso "amico" .


1
No, l'oggetto memento non ha bisogno e non dovrebbe avere alcun accesso all'oggetto. È l'oggetto che si serializza nel ricordo e si diserializza di nuovo. E il ricordo stesso è solo una stupida borsa di proprietà. Nessuno dei due dovrebbe avere più dell'accesso pubblico all'altro.
Jan Hudec,

@JanHudec Ho dichiarato letteralmente "è -> uno <- dei possibili modi per ottenere il modello Memento" .
Tulains Córdova,

E ancora un altro modo possibile per ottenere il modello Memento è rendere tutto pubblico. Cioè, non riesco a vedere il tuo punto.
Thomas Eding,

-1

simmetria?

È raro avere bisogno di tale accesso, motivo per cui l'accesso predefinito viene utilizzato così raramente. Ma a volte i framework lo richiedono per il codice generato, in cui le classi wrapper sono collocate nello stesso pacchetto che interagiscono direttamente con le classi, andando ai membri anziché agli accessori per motivi di prestazioni. Pensa a GWT.


1
grazie, hai un esempio? sembra che possa essere realizzato da un accessore "predefinito" piuttosto che "protetto"
jramoyo,

-1

La gerarchia di incapsulamento di Java è ben definita:

Classe -> Pacchetto -> Ereditarietà

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

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

Ha senso da un 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 lo sta subclassando. Nel primo caso è probabile che le classi siano scritte dallo stesso autore o almeno dai programmatori della stessa organizzazione.


1
cosa significa "solo una forma più debole ..." ?
moscerino del
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.