Conversioni di String in Java
Converti tra stringhe Java e primitivi — Integer.parseInt, Double.parseDouble, String.valueOf e altro ancora.
La maggior parte dei dati arriva come testo e la maggior parte dei dati parte come testo. Tra questi due confini, di solito vive come qualcosa di più utile — un int, un double, un boolean, un LocalDate. Questo capitolo riguarda le due direzioni di conversione tra String e i tipi primitivi, i tipi wrapper e gli array di caratteri.
Esiste un piccolo vocabolario che vale la pena memorizzare; una volta conosciuto, la risposta alla domanda "come trasformo questa stringa in una X" è sempre una riga singola.
String → primitivo: la famiglia parse...
Ogni classe wrapper ha un metodo statico che accetta una String e restituisce il primitivo corrispondente:
int i = Integer.parseInt("42");
long l = Long.parseLong("9999999999");
double d = Double.parseDouble("3.14");
float f = Float.parseFloat("3.14");
short s = Short.parseShort("32000");
byte b = Byte.parseByte("127");
boolean ok = Boolean.parseBoolean("true"); // any case-insensitive "true"; everything else → falseQuesti sono gli strumenti standard. Lanciano NumberFormatException per input che non viene analizzato — inclusi null, stringhe vuote, stringhe con spazi, decimali specifici per la locale come "3,14" e valori fuori intervallo.
Integer.parseInt(" 42 "); // NumberFormatException — trim first
Integer.parseInt("3.0"); // NumberFormatException — that's a double
Integer.parseInt("1234567890123"); // NumberFormatException — overflows intInteger.parseInt accetta un secondo argomento opzionale per la base:
Integer.parseInt("ff", 16); // 255
Integer.parseInt("1010", 2); // 10
Integer.parseInt("777", 8); // 511 — the radix is the second arg, not a prefix
Integer.parseInt("0x1A", 16); // NumberFormatException — no "0x" prefix; pass just "1A"parseInt legge le cifre letterali nella base che passi; non comprende i prefissi del codice sorgente come 0x o uno 0 iniziale per l'ottale. Quindi Integer.parseInt("0777", 8) va bene — lo 0 iniziale è semplicemente un'altra cifra ottale — ma Integer.parseInt("0x1A", 16) lancia un'eccezione, perché x non è una cifra in base 16. Rimuovi qualsiasi prefisso tu stesso prima di fare il parsing.
Per l'interpretazione senza segno del bit alto su valori a 32 e 64 bit, il JDK aggiunge Integer.parseUnsignedInt e Long.parseUnsignedLong.
String → wrapper: valueOf vs parse...
Il metodo statico valueOf su ogni wrapper esegue lo stesso parsing ma restituisce il tipo wrapper:
Integer n = Integer.valueOf("42"); // Integer, not int
Double d = Double.valueOf("3.14"); // Double, not doubleI due differiscono in tre punti che occasionalmente contano:
- Tipo restituito.
parseInt→int,valueOf→Integer. Scegli quello che si adatta alla destinazione. - Cache.
Integer.valueOf(int)eLong.valueOf(long)memorizzano nella cache i numeri piccoli (-128..127per impostazione predefinita), restituendo la stessa istanza diIntegerper chiamate ripetute.parseIntrestituisce un primitivo, quindi la cache non è rilevante — ma quando si stava comunque inscatolando il risultato,valueOfè più economico. - Comportamento in caso di overflow. Identico — entrambi lanciano
NumberFormatException.
Regola pratica: se vuoi un primitivo, usa parseInt; se vuoi un wrapper, usa valueOf. Non scrivere Integer.valueOf(Integer.parseInt(s)) — è il codice di due parsing per il risultato di uno.
Primitivo → String: String.valueOf e simili
La direzione inversa ha due strumenti ben nominati:
String s1 = String.valueOf(42); // "42"
String s2 = String.valueOf(3.14); // "3.14"
String s3 = String.valueOf(true); // "true"
String s4 = String.valueOf('a'); // "a"
String s5 = String.valueOf(new char[]{'h','i'}); // "hi"
String s6 = String.valueOf((Object) null); // "null" — NOT an NPEOgni wrapper ha anche un toString che accetta il primitivo:
Integer.toString(42); // "42"
Integer.toString(255, 16); // "ff" — radix overload
Long.toString(123456789L); // "123456789"
Double.toString(3.14); // "3.14"E naturalmente, la concatenazione con "" funziona:
String s = "" + 42; // "42"String.valueOf è il più generale e lo stile più difendibile — non lancia mai su null e gestisce ogni primitivo più Object. Per la formattazione numerica in cui hai bisogno di larghezza, segno, raggruppamento o decimali specifici per la locale, usa invece String.format.
Stringhe e char
Un char è un primitivo (un'unità di codice UTF-16); una String è una sequenza di essi. Conversioni in entrambe le direzioni:
String s = String.valueOf('a'); // "a"
char c1 = "abc".charAt(0); // 'a'
char[] arr = "abc".toCharArray(); // ['a', 'b', 'c']
String s2 = new String(arr); // "abc"
String s3 = new String(arr, 1, 2); // "bc" — offset + countString.toCharArray() restituisce sempre un array fresco e modificabile. È lo strumento giusto quando hai bisogno di mutare i caratteri (ad esempio per cancellare un buffer password) — la String stessa non può darti una vista mutabile.
Stringhe e byte
I byte sono il formato di rete; le stringhe sono la forma in memoria. La conversione implica sempre un charset, e la risposta giusta è sempre passarlo esplicitamente:
byte[] bytes = "héllo".getBytes(StandardCharsets.UTF_8);
String back = new String(bytes, StandardCharsets.UTF_8);Le versioni senza argomenti (getBytes(), new String(bytes)) usano il charset predefinito della JVM, che dipende dalla piattaforma ed è una fonte costante di bug "funziona sulla mia macchina". Passa sempre un Charset. UTF-8 è il valore predefinito corretto per il nuovo codice; i sistemi legacy a volte hanno bisogno di Latin-1 o di una delle code page Windows.
Conversioni che non esistono
Alcune conversioni che le persone cercano e che non sono disponibili:
int→charper valore cifra.(char) 5è il carattere di controllo al code point 5, non'5'. UsaCharacter.forDigit(5, 10)oppure(char) ('0' + 5).char→intper valore cifra.(int) '5'è 53, non 5. UsaCharacter.digit('5', 10)oppure'5' - '0'.String→ numerico con valore predefinito in caso di errore. Java non fornisce unparseIntOrDefault. La versione idiomatica è un piccolo helper:
static int parseIntOr(String s, int fallback) {
try { return Integer.parseInt(s); }
catch (NumberFormatException e) { return fallback; }
}La conversione non è validazione
Il singolo errore più grande nella conversione da stringa a primitivo è trattare un parsing riuscito come una validazione riuscita. Integer.parseInt("0") restituisce 0 per qualsiasi input che l'utente digita come zero, inclusi "0", "00", "+0" e "-0" — tutti analizzano allo stesso valore. Se ti importa se l'input era canonico, esegui il parsing e poi riconverti:
int v = Integer.parseInt(input);
if (!Integer.toString(v).equals(input)) {
throw new IllegalArgumentException("non-canonical integer: " + input);
}Per gli input numerici provenienti da moduli rivolti all'utente, valida prima il formato (regex, lunghezza, caratteri consentiti), poi esegui il parsing. Non affidarti al parser per dirti se l'input era ragionevole.
Un esempio pratico
Un programma che converte in entrambe le direzioni tra i tipi più comuni, incluso un helper robusto con valore predefinito in caso di errore, un round-trip esadecimale e un round-trip byte/string con UTF-8.
Tre dettagli da notare nell'output. La stringa héllo fa il round-trip attraverso UTF-8 correttamente — cinque caratteri diventano sei byte (la é occupa due), e i byte risultanti vengono analizzati di nuovo negli stessi cinque caratteri. String.valueOf((Object)null) produce "null" invece di lanciare — è deliberato; è per questo che "x = " + nullable non si blocca. E l'helper parseIntOr gestisce input errati, input con spazi e input null senza che il sito chiamante debba saperlo.
Cosa c'è dopo
Il capitolo che chiude la Parte 9 copre i due metodi "string in, list out / list in, string out" più utilizzati nella libreria standard — e le varianti con espressioni regolari che si trovano al di sotto. Continua con Java String split() e join().