2009-05-16 20 views
49

La classe java.util.Properties è pensata per rappresentare una mappa in cui le chiavi e i valori sono entrambe stringhe. Questo perché gli oggetti Properties vengono utilizzati per leggere i file .properties, che sono file di testo.Perché java.util.Properties implementa Map <Object, Object> e non Map <String, String>

Quindi, perché in Java 5 hanno adattato questa classe per implementare Map<Object,Object> e non Map<String,String>?

I javadoc stati:

perché le proprietà eredita da Hashtable, la put e metodi putAll possono essere applicati a un oggetto di proprietà. Il loro uso è fortemente scoraggiato in quanto consentono al chiamante di inserire voci le cui chiavi o valori non sono stringhe. Al suo posto dovrebbe essere usato il metodo setProperty. Se il metodo store o save viene richiamato su un oggetto Properties "compromesso" che contiene una chiave o un valore non String, la chiamata avrà esito negativo.

Poiché le chiavi e i valori sono entrambi supposti essere stringhe, perché non applicarli staticamente utilizzando il tipo generico corretto?

Credo che fare Properties attuare Map<String,String> non sarebbe completamente compatibile con il codice scritto per la pre-Java 5. Se si dispone di codice più vecchio che si attacca non stringhe in una proprietà dell'oggetto allora che il codice non sarebbe più compilare con Java 5. Ma ... non è una buona cosa? L'intero punto dei generici non è quello di cogliere errori di questo tipo al momento della compilazione?

risposta

50

Perché lo hanno fatto in fretta nei primi giorni di Java e non si sono resi conto di quali sarebbero state le quattro versioni successive.

I generici avrebbero dovuto far parte del progetto di Java fin dall'inizio, ma la funzionalità è stata abbandonata perché troppo complicata e, al momento, non necessaria. Di conseguenza, un sacco di codice nelle librerie standard viene scritto con l'assunzione di raccolte non generiche.Ha preso il linguaggio prototipo "Pizza" di Martin Odersky per mostrare come potrebbero essere fatti abbastanza bene mantenendo una compatibilità all'indietro quasi perfetta, sia con il codice Java che con il bytecode. Il prototipo ha portato a Java 5, in cui le classi di collezioni sono state riadattate con generici in un modo che consentiva al vecchio codice di continuare a funzionare.

Purtroppo, se dovessero rendere retroattivamente Properties ereditare da Map<String, String>, quindi il seguente codice precedentemente valida sarebbe smettere di lavorare:

Map<Object, Object> x = new Properties() 
x.put("flag", true) 

Perché qualcuno dovrebbe fare che è al di là di me, ma l'impegno di Sun per la compatibilità a ritroso nel Java è andato oltre l'eroico nell'inutile.

Ciò che ora viene apprezzato dagli osservatori più istruiti è che Properties non dovrebbe mai essere stato ereditato da Map. Dovrebbe invece avvolgere intorno a Map, esponendo solo quelle funzionalità di Map che hanno senso.

Da quando reinventa Java, Martin Odersky ha continuato a creare il nuovo linguaggio Scala, che è più pulito, eredita meno errori e apre nuovi orizzonti in numerose aree. Se stai riscontrando fastidi di Java, dai un'occhiata.

+1

In realtà Map x = new Properties() funziona in entrambi i modi. La sua gente mette cose come properties.put ("flag", Boolean.TRUE); Ho visto persone mettere tutti i tipi di dati in un oggetto Proprietà, ma mai una chiave non String. ;) –

+0

Ho aggiornato la mia risposta per renderlo più chiaro, grazie. –

+5

Aspetta un minuto! Map x = new Properties() non è legale pre-Java 5, la sintassi è stata introdotta in Java 5. Quindi l'istruzione "codice precedentemente valido" non è corretta. –

2

Motivo: Liskov substitution principle e retrocompatibilità. Properties estende Hashtable e pertanto deve accettare tutti i messaggi accettati da Hashtable e ciò significa accettare put(Object, Object). E deve estendere semplicemente Hashtable invece di Hashtable<String, String> perché i Generics sono stati implementati in modo compatibile con il downward tramite type erasure, quindi una volta che il compilatore ha fatto il suo dovere, non ci sono generici.

+3

non credo che il principio di sostituzione Liskov ha nulla a che fare con esso. Java felicemente consente di affinare i tipi generici quando si eredita una classe. Ecco perché esiste la sintassi e . –

4

Compatibilità con le versioni precedenti.

27

Originariamente previsto che Properties si estenda effettivamente Hashtable<String,String>. Sfortunatamente l'implementazione dei metodi bridge ha causato un problema. Properties definito in questo modo fa sì che javac generi metodi sintetici. Properties deve definire, ad esempio, un metodo get che restituisce un valore String ma deve sostituire un metodo che restituisce Object. Quindi viene aggiunto un metodo bridge sintetico.

Supponi di avere una lezione scritta nei cattivi vecchi 1,4 giorni. Hai sovrascritto alcuni metodi in Properties. Ma ciò che non hai fatto è sovrascritto dai nuovi metodi. Ciò porta a comportamenti non voluti. Per evitare questi metodi bridge, Properties estende Hashtable<Object,Object>. Allo stesso modo Iterable non restituisce un valore (di sola lettura) SimpleIterable, poiché ciò avrebbe aggiunto metodi alle implementazioni Collection.

+2

Buona spiegazione. Mi sono interrogato anche su questo. –

13

una battuta (due-liner per nessun avviso) per la creazione di Map da Proprietà:

@SuppressWarnings({ "unchecked", "rawtypes" }) 
Map<String, String> sysProps = new HashMap(System.getProperties()); 
+0

Non risponde alla domanda. Ma risponde a ciò che ha causato la domanda in primo luogo. Dritto al punto. –

+1

I "rawtype" sono necessari qui? (Almeno nella mia IDEA IntelliJ, "deselezionata" è sufficiente per sopprimere eventuali avvisi.) – Jonik

Problemi correlati