2014-06-18 17 views
94

stavo attraversando questa domanda Is there a way to override class variables in Java? Il primo commento con 36 upvotes era:Perché non dovremmo usare protetto statico in Java

Se avete mai visto un protected static, correre.

Qualcuno può spiegare perché è un protected static disapprovato?

+5

Non c'è niente di sbagliato in un campo statico protetto, a patto che sia 'finale'. Un campo statico mutabile condiviso tra le classi è sicuramente motivo di preoccupazione. Non è probabile che più classi che aggiornano un campo statico siano affidabili o facili da seguire, soprattutto perché la presenza di qualsiasi campo o metodo protetto implica che la classe debba essere estesa da classi in altri pacchetti, possibilmente classi non sotto il controllo del autore della classe che contiene il campo protetto. – VGR

+5

@ VGR, 'final' non significa che il campo è immutabile. Puoi sempre modificare l'oggetto referenziato da una variabile di riferimento 'finale'. – Zeeshan

+0

@VGR Non sono d'accordo. L'UNICA ragione per cui si dovrebbe creare una variabile statica è di avere accesso ad essa da un altro pacchetto solo per ereditarietà, e l'accesso a un singolo campo NON dovrebbe essere la ragione dell'ereditarietà. È un design imperfetto, IMO, e se ricorrerai a ciò, dovresti probabilmente ripensare la struttura della tua applicazione. Questa però è solo la mia opinione. –

risposta

62

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

pensare a che cosa significa static:

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

pensare a che cosa significa protected:

Questa variabile può essere visto da questa classe, classi dello stesso package e le classi che mi estendere.

I due significati non si escludono esattamente a vicenda ma è piuttosto vicino.

L'unico caso che posso vedere dove è possibile utilizzare i due insieme è se si avesse una classe astratta che è stata progettata per essere estesa e la classe di estensione 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 - provare 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; 
    } 
} 

vedrete 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 dover qualificare il nome - ma non lo fa t ereditare o ottenere la propria copia. Sta guardando la stessa identica variabile.

+2

Ragazzi siete impiccati all'eredità. Il caso tipico in cui questo è visto è qualcuno che vuole l'accesso al pacchetto senza renderlo pubblico (ad esempio un test unitario). – spudone

+3

@spudone ma i test unitari sono generalmente inseriti nello stesso pacchetto. Per dare loro l'accesso basta usare il livello di accesso predefinito (pacchetto). Protetto consente anche l'accesso a sottoclassi che non sono richieste o rilevanti per il test dell'unità. –

+0

Mentre risponde correttamente alla domanda, personalmente non ottengo questa riga di codice: 'Test.test =" modificato ";'. Come può il 'programma di classe 'essere in grado di accedere all''attributo * protected * test'? Gli attributi * protected * non dovrebbero essere accessibili solo dai bambini della classe * Test *? Oppure perché * * * può essere visto da * "classi nello stesso pacchetto" *, come citato sopra? (scusa se la domanda è stupida, quasi non conosco Java) – Kamafeather

6

membri statiche non vengono ereditate, e membri protetti sono visibili solo a sottoclassi (e naturalmente la classe contenente), quindi un protected static ha la stessa visibilità static, suggerendo un equivoco dal codificatore.

+1

Potresti fornire una fonte per la tua prima affermazione? In un test rapido, un int statico protetto è stato ereditato e riutilizzato in una sottoclasse senza problemi. – hiergiltdiestfu

+0

L'elemento statico protetto ha la stessa visibilità del pacchetto statico privato, non privato statico. Per aggiungere questo, se stai usando protetto e statico, è meglio rimuovere semplicemente il modificatore di accesso per renderlo pacchetto-privato (se le tue intenzioni erano di renderlo accessibile all'interno del pacchetto) –

+2

Uhm ... no. pacchetto privato e 'protetto' non sono la stessa cosa. Se si dice 'static', il campo è visibile solo per sottoclassi nello stesso pacchetto. –

10

Non vedo un motivo 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:

(a cura di dividere in pacchetti separati, per rendere l'uso di protected chiaro)

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 tale :

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 pensati per essere usati direttamente come metodo polimorfico, ma piuttosto come metodi "di utilità" che offrono le implementazioni predefinite che fanno parte di un calcolo più complesso e servono come "elementi costitutivi" dell'attuazione reale.
  • I metodi non devono essere public, perché sono un dettaglio di implementazione. E non possono essere private perché dovrebbero essere chiamati dalle classi di estensione. Inoltre non possono avere visibilità "predefinita", perché quindi non saranno accessibili per le classi di estensione in altri pacchetti.

(EDIT: Si potrebbe supporre che il commento originale di cui solo per campi, e non a metodi - allora, tuttavia, era troppo generale)

+0

In questo caso, è necessario definire le implementazioni predefinite come 'protected final' (poiché non le si vuole sovrascrivere), non' static'. Il fatto che un metodo non usi variabili di istanza non significa che dovrebbe essere 'statico' (anche se * può *). –

+2

@Thomas Certo, in questo caso, anche questo sarebbe possibile. In generale: questo è certamente parzialmente soggettivo, ma la mia regola generale è: quando un metodo non è inteso per essere usato polimorficamente, e può essere statico, quindi lo faccio statico. Al contrario di renderlo 'final', non solo chiarisce che il metodo non è destinato a essere sovrascritto, ma * inoltre * rende chiaro al lettore che il metodo non usa variabili di istanza. Quindi, \t in modo succinto: semplicemente non c'è motivo per * non * renderlo statico. – Marco13

+1

* Quando un metodo non è destinato a essere utilizzato in modo polimorfico, e può essere statico, quindi lo faccio statico. * - Questo ti porterà nei guai quando inizi a utilizzare i framework mock per il test delle unità. Ma questo ci porta ad un argomento diverso ... –

3

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

23

È malvisto perché è contraddittorio.

Fare una variabile protected implica verrà utilizzato all'interno del pacchetto o sarà ereditato entro una sottoclasse.

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

L'unica situazione che ho trovato utile è se si dichiarava una classe da utilizzare per avviare l'applicazione (come Java23X Application#launch e si desiderava essere in grado di avviarsi da una sottoclasse. il metodo è anche final per non consentire hiding. Ma questo non è "la norma", e fu probabilmente implementato per evitare che l'aggiunta di una maggiore complessità con l'aggiunta di un nuovo modo di lanciare le applicazioni.

per visualizzare i livelli di accesso di ogni modificatore, vedi this: The Java Tutorials - Controlling Access to Members of a Class

+4

Non capisco come' static' eliminerebbe le intenzioni di ereditarlo. Dato che la mia sottoclasse in un altro pacchetto richiede ancora l'accesso al campo di super per 'protected', anche se è' static'. 'package-private' non può essere d'aiuto –

+0

@AoboYang Hai ragione, ecco perché alcune persone usano' protected static'. Ma è un odore di codice, quindi la parte "* run *". I modificatori di accesso e l'ereditarietà sono due soggetti diversi. Sì, non sarai in grado di accedere a un membro statico di una super classe se fosse 'package-private'. Ma non si dovrebbe fare affidamento sull'eredità per fare riferimento ai campi 'static' in primo luogo; è un segno di design scadente. Noterai che i tentativi di sovrascrivere i metodi 'static' non danno risultati, il che è un chiaro segnale che l'ereditarietà non è basata sulla classe. Se è necessario accedere al di fuori della classe o del pacchetto, dovrebbe essere 'public' –

+3

Ho una classe con alcune funzioni di utilizzo' statico privato 'all'inizio, ma penso che qualcuno potrebbe voler migliorare o personalizzare dalla mia classe e queste funzioni utili può anche fornire convenienza a loro. 'public' potrebbe non essere appropriato poiché i metodi util non sono per l'utente delle istanze della mia classe. Potresti aiutarmi a capire un buon design invece di 'protected'? Grazie –

3

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

Alcune persone generalmente evitano di utilizzare protected per vari motivi e alcune persone pensano non definitive static variabili dovrebbe essere evitato a tutti i costi (io personalmente solidale con quest'ultimo in una certa misura), quindi credo che la combinazione di protected e static deve guardare male^2 a quelli che appartengono a entrambi i gruppi.

0

Non c'è niente di sbagliato nell'avere 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à.