Perché non dovremmo usare la static protetta in java

Stavo passando per questa domanda C’è un modo per sostituire le variabili di class in Java? Il primo commento con 36 upvotes è stato:

Se mai vedi una protected static , corri.

Qualcuno può spiegare perché una protected static disapprova?

È più una questione stilistica che un problema diretto. Suggerisce di non aver adeguatamente pensato a quello che sta succedendo alla class.

Pensa a cosa significa static :

Questa variabile esiste a livello di class, non esiste separatamente per ogni istanza e non ha un’esistenza indipendente nelle classi che mi estendono .

Pensa a cosa significa protected :

Questa variabile può essere vista da questa class, classi nello stesso pacchetto e classi che mi estendono .

I due significati non sono esattamente mutuamente esclusivi ma è piuttosto vicino.

L’unico caso in cui posso vedere dove è ansible utilizzare i due insieme è se si avesse una class astratta che è stata progettata per essere estesa e la class extendente potrebbe quindi modificare il comportamento utilizzando le costanti definite nell’originale. Anche in questo caso è una ragione molto debole, anche se quasi sicuramente sarebbe meglio avere le costanti come pubbliche. Ciò rende tutto più pulito e consente alle persone di subclassare una maggiore flessibilità.

Per espandere e spiegare il primo punto – prova questo codice di esempio:

 public class Program { public static void main (String[] args) throws java.lang.Exception { System.out.println(new Test2().getTest()); Test.test = "changed"; System.out.println(new Test2().getTest()); } } abstract class Test { protected static String test = "test"; } class Test2 extends Test { public String getTest() { return test; } } 

Vedrai i risultati:

 test changed 

Provalo tu stesso su: https://ideone.com/KM8u8O

La class Test2 è in grado di accedere al test membri statici da Test senza la necessità di qualificare il nome, ma non eredita o ottiene la propria copia. Sta guardando la stessa identica variabile.

È disapprovato perché è contraddittorio.

Rendere una variabile protected implica che verrà utilizzato all’interno del pacchetto o che verrà ereditato all’interno di una sottoclass .

Rendere la variabile static rende un membro della class, eliminando le intenzioni di ereditarlo . Questo lascia solo l’intenzione di essere usato all’interno di un pacchetto , e abbiamo il package-private per questo (nessun modificatore).

L’unica situazione che ho trovato utile è che stavi dichiarando una class che dovrebbe essere usata per lanciare l’applicazione (come il Application#launch di JavaFX, e volevo solo essere in grado di lanciare da una sottoclass. è anche final per non hide il nascondiglio, ma non è “la norma”, e probabilmente è stato implementato per impedire l’aggiunta di ulteriori complessità aggiungendo un nuovo modo di lanciare le applicazioni.

Per vedere i livelli di accesso di ciascun modificatore, vedere questo: Le esercitazioni Java – Controllo dell’accesso ai membri di una class

Non vedo una ragione particolare per cui questo dovrebbe essere disapprovato. Ci possono sempre essere alternative per ottenere lo stesso comportamento, e dipenderà dalla reale architettura se queste alternative sono “migliori” di un metodo statico protetto o meno. Ma un esempio in cui un metodo statico protetto sarebbe ragionevole, almeno potrebbe essere il seguente:

(Modificato per dividere in pacchetti separati, per rendere l’uso di clearer protected )

 package a; import java.util.List; public abstract class BaseClass { public Integer compute(List list) { return computeDefaultA(list)+computeDefaultB(list); } protected static Integer computeDefaultA(List list) { return 12; } protected static Integer computeDefaultB(List list) { return 34; } } 

Derivato da quello:

 package ab; import java.util.List; import a.BaseClass; abstract class ExtendingClassA extends BaseClass { @Override public Integer compute(List list) { return computeDefaultA(list)+computeOwnB(list); } private static Integer computeOwnB(List list) { return 56; } } 

Un’altra class derivata:

 package ab; import java.util.List; import a.BaseClass; abstract class ExtendingClassB extends BaseClass { @Override public Integer compute(List list) { return computeOwnA(list)+computeDefaultB(list); } private static Integer computeOwnA(List list) { return 78; } } 

Il modificatore protected static può certamente essere giustificato qui:

  • I metodi possono essere static , perché non dipendono da variabili di istanza. Non sono pensati per essere usati direttamente come metodo polimorfico, ma piuttosto come metodi “di utilità” che offrono implementazioni predefinite che fanno parte di un calcolo più complesso e servono come “elementi costitutivi” dell’attuazione reale.
  • I metodi non dovrebbero essere public , perché sono un dettaglio di implementazione. E non possono essere private perché dovrebbero essere chiamati dalle classi estensive. Inoltre non possono avere visibilità “predefinita”, perché quindi non saranno accessibili per le classi di estensione in altri pacchetti.

(MODIFICA: Si potrebbe supporre che il commento originale si riferisse solo ai campi , e non ai metodi – quindi, tuttavia, era troppo generale)

I membri statici non sono ereditati e i membri protetti sono visibili solo alle sottoclassi (e ovviamente alla class contenente), quindi una protected static ha la stessa visibilità di quella static , suggerendo un equivoco da parte del codificatore.

Protetto viene utilizzato in modo che possa essere utilizzato in sottoclassi. Non vi è alcuna logica nel definire una statica protetta quando si utilizza nel contesto di classi concrete poiché è ansible accedere alla stessa variabile in modo statico. Tuttavia, il compilatore fornirà un avvertimento per accedere alla variabile statica di super class in modo statico.

In realtà non c’è niente di fondamentalmente sbagliato con la protected static . Se si desidera realmente una variabile statica o un metodo che è visibile per il pacchetto e tutte le sottoclassi della class dichiarante, andare avanti e renderlo protected static .

Alcune persone generalmente evitano l’uso protected per vari motivi e alcune persone pensano che static variabili static non definitive dovrebbero essere evitate con tutti i mezzi (io personalmente simpatizzo con quest’ultimo in una certa misura), quindi suppongo che la combinazione di protected e static debba apparire male ^ 2 a quelli che appartengono a entrambi i gruppi.

Non c’è niente di sbagliato nell’avere una protected static . Una cosa che molte persone trascurano è che potresti voler scrivere casi di test per metodi statici che non vuoi esporre in circostanze normali. Ho notato che questo è particolarmente utile per scrivere test per il metodo statico nelle classi di utilità.

Bene, come la maggior parte delle persone ha risposto:

  • mezzi protected – ‘ pacchetto-privato + visibilità per sottoclassi – la proprietà / comportamento è EREDITATO
  • mezzi static – ” il contrario dell’istanza – si tratta di una proprietà / comportamento di CLASSE, ovvero NON è EREDITATA

Pertanto sono leggermente contraddittori e incompatibili.

Tuttavia, recentemente mi sono imbattuto in un caso d’uso in cui avrebbe avuto senso usare questi due insieme. Immagina di voler creare una class abstract che sia un genitore per i tipi immutabili e che abbia un sacco di proprietà comuni ai sottotipi. Per implementare correttamente l’ immutabilità e mantenere la leggibilità, si potrebbe decidere di utilizzare il modello Builder .

 package X; public abstract class AbstractType { protected Object field1; protected Object field2; ... protected Object fieldN; protected static abstract class BaseBuilder> { private Object field1; // = some default value here private Object field2; // = some default value here ... private Object fieldN; // = some default value here public T field1(Object value) { this.field1 = value; return self();} public T field2(Object value) { this.field2 = value; return self();} ... public T fieldN(Object value) { this.fieldN = value; return self();} protected abstract T self(); // should always return this; public abstract AbstractType build(); } private AbstractType(BaseBuilder b) { this.field1 = b.field1; this.field2 = b.field2; ... this.fieldN = b.fieldN; } } 

E perché protected static ? Perché voglio un sottotipo non astratto di AbstactType che implementa il proprio Builder non astratto e si trova all’esterno del package X per poter accedere e riutilizzare il BaseBuilder .

 package Y; public MyType1 extends AbstractType { private Object filedN1; public static class Builder extends AbstractType.BaseBuilder { private Object fieldN1; // = some default value here public Builder fieldN1(Object value) { this.fieldN1 = value; return self();} @Override protected Builder self() { return this; } @Override public MyType build() { return new MyType(this); } } private MyType(Builder b) { super(b); this.fieldN1 = b.fieldN1; } } 

Ovviamente possiamo rendere pubblico il BaseBuilder ma poi arriviamo a un’altra affermazione contraddittoria:

  • Abbiamo una class non istantanea (abstract)
  • Forniamo un costruttore pubblico per questo

Quindi in entrambi i casi con protected static e costruttore public di una abstract class combiniamo affermazioni contraddittorie. È una questione di preferenze personali.

Tuttavia, continuo a preferire il costruttore public di una abstract class perché la protected static mi sembra più innaturale in un mondo OOD e OOP!