Perché non dovremmo usare static protetto in java
Stavo affrontando questa domanda C'è un modo per sovrascrivere le variabili di classe in Java? Il primo commento con 36 upvotes è stato:
Se vedi un
protected static
, esegui.
Qualcuno può spiegare perché un protected static
è disapprovato?
8 answers
È più una cosa stilistica che un problema diretto. Suggerisce che non hai pensato correttamente a cosa sta succedendo con la classe.
Pensa a cosa significa static
:
Questa variabile esiste a livello di classe, non esiste separatamente per ogni istanza e non ha un'esistenza indipendente in classi che estendono me.
Pensa a cosa significa protected
:
Questa variabile può essere vista da questa classe, classi in lo stesso pacchetto e le classi che mi estendono .
I due significati non si escludono esattamente a vicenda ma sono piuttosto vicini.
L'unico caso in cui posso vedere dove potresti usare i due insieme è se avessi una classe astratta progettata per essere estesa e la classe di estensione potrebbe quindi modificare il comportamento usando le costanti definite nell'originale. Anche allora è una ragione molto debole però come sarebbe ancora quasi certamente meglio avere le costanti come pubblico. Ciò rende tutto più pulito e consente alle persone di classificare in modo più flessibile.
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
Provate voi stessi a: https://ideone.com/KM8u8O
La classe Test2
è in grado di accedere al membro statico test
da Test
senza bisogno di qualificare il nome, ma non eredita o ottiene la propria copia. Sta guardando esattamente lo stesso variabile.
Non è visto di buon occhio perché è contraddittorio.
Creare una variabile protected
implica che verrà utilizzata all'interno del pacchetto o sarà ereditata all'interno di una sottoclasse .
Rendere la variabile static
la rende un membro della classe, eliminando le intenzioni di ereditarla. Questo lascia solo l'intenzione di essere usato all'interno di un pacchetto, e abbiamo package-private
per quello (nessun modificatore).
L'unica situazione per cui ho potuto trovare questo utile è se si stava dichiarando una classe che dovrebbe essere utilizzata per avviare l'applicazione (come Application#launch
di JavaFX e si voleva solo essere in grado di avviare da una sottoclasse. In tal caso, assicurarsi che il metodo sia anche final
per disabilitare nascondendo. Ma questa non è "la norma", ed è stata probabilmente implementata per evitare di aggiungere più complessità aggiungendo un nuovo modo per avviare le applicazioni.
Per vedere i livelli di accesso di ciascun modificatore, vedere questo: Le esercitazioni Java-Controllo dell'accesso ai membri di un Classe
Non vedo un motivo particolare per cui questo dovrebbe essere visto di buon occhio. Ci possono sempre essere alternative per ottenere lo stesso comportamento e dipenderà dall'effettiva 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 più chiaro l'uso di protected
)
package a;
import java.util.List;
public abstract class BaseClass
{
public Integer compute(List<Integer> list)
{
return computeDefaultA(list)+computeDefaultB(list);
}
protected static Integer computeDefaultA(List<Integer> list)
{
return 12;
}
protected static Integer computeDefaultB(List<Integer> list)
{
return 34;
}
}
Derivato da ca:
package a.b;
import java.util.List;
import a.BaseClass;
abstract class ExtendingClassA extends BaseClass
{
@Override
public Integer compute(List<Integer> list)
{
return computeDefaultA(list)+computeOwnB(list);
}
private static Integer computeOwnB(List<Integer> list)
{
return 56;
}
}
Un'altra classe derivata:
package a.b;
import java.util.List;
import a.BaseClass;
abstract class ExtendingClassB extends BaseClass
{
@Override
public Integer compute(List<Integer> list)
{
return computeOwnA(list)+computeDefaultB(list);
}
private static Integer computeOwnA(List<Integer> 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 destinati ad essere utilizzati direttamente come metodo polimorfico, ma piuttosto sono metodi di "utilità" che offrono implementazioni predefinite che fanno parte di un calcolo più complesso e fungono da "blocchi costitutivi" dell'implementazione effettiva. - I metodi dovrebbero non essere
public
, perché sono un dettaglio di implementazione. E non possono essereprivate
perché dovrebbero essere chiamati dalle classi estendenti. Inoltre, non possono avere visibilità "predefinita", perché non saranno accessibili per le classi di estensione in altri pacchetti.
(EDIT: 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 classe contenente), quindi a protected static
ha la stessa visibilità di static
, suggerendo un malinteso da parte del codificatore.
Protected viene utilizzato in modo che possa essere utilizzato in sottoclassi. Non c'è logica nel definire una statica protetta quando si utilizza nel contesto di classi concrete in quanto è possibile accedere alla stessa variabile è un modo statico.Tuttavia il complier darà un avviso per accedere alla variabile statica super class in modo statico.
In realtà non c'è nulla di fondamentalmente sbagliato in protected static
. Se vuoi davvero una variabile statica o un metodo visibile per il pacchetto e tutte le sottoclassi della classe dichiarante, vai avanti e fallo protected static
.
Alcune persone generalmente evitano di usare protected
per vari motivi e alcune persone pensano che le variabili static
non definitive dovrebbero essere evitate con tutti i mezzi (personalmente simpatizzo con quest'ultimo in una certa misura), quindi immagino che la combinazione di protected
e static
debba guardare bad^2 a quelli che appartengono a entrambi i gruppi.
Non c'è niente di sbagliato nell'avere protected static
. Una cosa che molte persone stanno trascurando è 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 della gente ha risposto:
-
protected
significa - ' pacchetto - privato + visibilità alle sottoclassi - la proprietà / il comportamento è EREDITATO ' -
static
significa- ' l'opposto dell'istanza - è una proprietà/comportamento di CLASSE, CIOÈ NON è EREDITATA '
Pertanto sono leggermente contraddittori e incompatibili.
Tuttavia, recentemente sono arrivato a un caso d'uso in cui potrebbe avere senso usare questi due insieme. Immagina di voler creare un abstract
classe che è un genitore per immutabile tipi e ha un sacco di proprietà che sono comuni ai sottotipi. Per implementare immutabilità correttamente e mantenere 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<T extends BaseBuilder<T>> {
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 costruttore non astratto e si trova all'esterno di package X
per essere in grado di accedere e riutilizzare il BaseBuilder
.
package Y;
public MyType1 extends AbstractType {
private Object filedN1;
public static class Builder extends AbstractType.BaseBuilder<Builder> {
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;
}
}
Naturalmente possiamo rendere pubblico il BaseBuilder
ma poi arriviamo ad altre dichiarazioni contraddittorie:
- Abbiamo una classe non istanziabile (abstract)
- Forniamo un costruttore pubblico per esso
Quindi in entrambi i casi con protected static
e public
costruttore di un abstract class
combiniamo dichiarazioni contraddittorie. È una questione di preferenze personali.
Tuttavia, preferisco ancora il public
costruttore di un abstract class
perché il protected static
per me si sente più innaturale in un mondo OOD e OOP !