Il codice incriminato:
public class Test {
public static void main(String[] args) {
String a1 = "test";
String a2 = "test";
System.out.println(a1 == a2);
a2 = new String("test");
System.out.println(a1 == a2);
}
}
Il primo confronto ritorna true, mentre il secondo false: ora, io avevo sempre pensato che una stringa inizializzata nel primo modo fosse semplicemente una scorciatoia al posto di new, invece a quanto pare sono due cose profondamente diverse. Il primo confronto dovrebbe, a norma di legge, confrontare i riferimenti, non i contenuti, ed invece non è così (nonostante i due oggetti siano dichiarati separatamente).
Perchè l’ho chiamato tradimento? Bè, perchè questo viola chiaramente il presupposto degli oggetti in Java: per quanto le stringhe siano un tipo base, sono comunque oggetti a tutti gli effetti, o almeno lo erano prima di questa scoperta… inutile dire che vale lo stesso fattaccio anche per gli oggetti che incorporano i tipi base (Integer vs. int). Questo dimostra comunque che conviene usare equals, dato che se una stringa viene ricreata l’== non funziona più, ma resta un notevole pacco.
Un ringraziamento a Maintux, che ha insinuato il seme del dubbio, ed a Sante che ha condiviso un’istanza di Eclipse all’uopo.
questa cosa ti farà sentire ancora più tradito 🙂
http://blackfire.soup.io/post/1636660/I-love-java
Per una volta, non è un problema di Java, e il comportamento in questo caso coerente con le specifiche del linguaggio.
Primo: le stringhe NON sono un tipo primitivo in Java (http://download.oracle.com/docs/cd/E17409_01/javase/tutorial/java/nutsandbolts/datatypes.html : ).
Il problema in cui sei cascato è la gestione dei string literal (cioè delle costanti-stringa) da parte del compilatore, e la gestione delle rappresentazioni canoniche delle stringhe.
Infatti:
http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#101084
Each string literal is a reference (§4.3) to an instance (§4.3.1, §12.5) of class String (§4.3.3). String objects have a constant value. String literals-or, more generally, strings that are the values of constant expressions (§15.28)-are “interned” so as to share unique instances, using the method String.intern.
….
Literal strings within the same class (§8) in the same package (§7) represent references to the same String object (§4.3.1).
In pratica, nel tuo programma, a1 ed a2 sono alias, ed è il compilatore a renderli tali. Non è irragionevole: è una ottimizzazione comune, segue comunque le specifiche del linguaggio Java, ed è possibile farlo in maniera safe perché le stringhe sono immutabili.
Se provi il programma d’esempio nella Java Language Specification hai degli esempi ancora più “strambi”.
Lezione da imparare? Per tipi immutabili NON si usa MAI l’==, nemmeno per le stringhe. Bisogna usare sempre il metodo equals. L’alternativa è confrontare con l’== gli oggetti canonici corrispondenti, che si recuperano con il metodo String intern() di String… che, by the way, è l’implementazione stessa del metodo equals di String.
Concordo pienamente sulla lezione da imparare (che peraltro già applicavo, il dubbio era venuto da una discussione), e pur non avendo fatto ricerche in merito pensavo proprio che l’implementazione condividesse le string-literals.