Java Conversão de Tipos

Você já viu que cada variável tem um tipo fixo. Mas os programas vivem trocando valores de um tipo para outro: uma soma de inteiros que precisa virar média decimal, uma idade digitada como texto que precisa virar número, um número que precisa virar texto para aparecer numa mensagem. Esta aula reúne todas essas conversões: as que o Java faz sozinho, as que você pede com um casting e as que exigem um método.

Você vai aprender
  • A conversão automática (alargamento): quando o Java converte sem você pedir.
  • O casting (estreitamento): como pedir uma conversão que pode perder informação, e o que se perde.
  • Como transformar número em texto e texto em número (Integer.parseInt, String.valueOf).
  • Como converter entre char e int.
  • A promoção em contas: por que byte + byte dá int.

A ideia: potes de tamanhos diferentes

Volte à imagem dos potes de Tipos Primitivos. Despejar o conteúdo de um pote pequeno num pote grande é sempre seguro: cabe tudo. Despejar um pote grande num pequeno pode transbordar. O Java segue exatamente essa lógica:

DireçãoNome técnicoQuem fazRisco
Do menor para o maior (int → double)Alargamento (widening)O Java, sozinhoNenhum (quase: veja float adiante)
Do maior para o menor (double → int)Estreitamento (narrowing)Você, com um castingPerder a parte decimal ou "dar a volta"
Entre texto e número ("42" ↔ 42)Conversão por métodoVocê, chamando um métodoO texto pode não ser um número

1. Conversão automática (alargamento)

Quando o valor vai para um tipo maior, o Java converte sem você escrever nada:

public class Main {
    public static void main(String[] args) {
        int alunos = 32;
        long grande = alunos;
        double media = alunos;
        float f = alunos;
        char letra = 'A';
        int codigo = letra;
        System.out.println(grande);
        System.out.println(media);
        System.out.println(f);
        System.out.println(codigo);
    }
}
$ javac Main.java && java Main
32
32.0               « o int virou double
32.0
65                 « o char virou o seu código Unicode

O caminho do alargamento segue esta ordem; qualquer tipo pode ir automaticamente para qualquer um à sua direita:

byte → short → int → long → float → double
               ↑
             char
DeVai sozinho para
byteshort, int, long, float, double
shortint, long, float, double
charint, long, float, double
intlong, float, double
longfloat, double
floatdouble
booleannenhum (não converte para nada, nem de nada)

Repare que char e short não convertem automaticamente um no outro: char não tem negativos e short não chega a 65.535, então nenhum "cabe" inteiro no outro.

A exceção: inteiros grandes em float e double

long → float é automático, mas o float só guarda 6 a 7 dígitos exatos. O valor cabe (a faixa é enorme), mas os dígitos finais se perdem:

$ javac Main.java && java Main
123456789123456789         « o long original
1.2345679E17               « no float: só 8 dígitos sobreviveram
1.2345678912345678E17      « no double: 17 dígitos, ainda falta o último 9
1.2345679E8                « até um int (123456789) perde dígitos num float

Para números inteiros grandes, fique em long. Veja double e float.


2. Casting: conversão com perda (estreitamento)

Do maior para o menor, o Java não converte sozinho. Ele recusa com um erro que você já conhece:

public class Main {
    public static void main(String[] args) {
        double preco = 9.99;
        int inteiro = preco;
    }
}
$ javac Main.java
Main.java:4: error: incompatible types: possible lossy conversion from double to int
        int inteiro = preco;
                      ^
1 error

Se você sabe o que está fazendo e aceita a perda, pode forçar a conversão escrevendo o tipo de destino entre parênteses antes do valor. Isso se chama casting (do inglês cast, "moldar", como derramar metal num molde):

int inteiro =
A variável de destino, do tipo menor.
(int)
O operador de casting: "converta o que vem a seguir para int; eu assumo o risco".
preco;
O valor a converter.
public class Main {
    public static void main(String[] args) {
        double preco = 9.99;
        int inteiro = (int) preco;
        System.out.println(inteiro);
        System.out.println((int) -9.99);
        System.out.println(Math.round(9.99));
        System.out.println((int) Math.round(9.49));
        System.out.println((byte) 200);
        System.out.println((short) 70000);
        System.out.println((int) 3.0e10);
        System.out.println((long) 3.0e10);
        System.out.println((int) 'A');
        System.out.println((char) 66);
        System.out.println((double) 7 / 2);
        System.out.println((double) (7 / 2));
    }
}
$ javac Main.java && java Main
9                  « (int) 9.99: a parte decimal foi CORTADA, não arredondada
-9                 « com negativos também corta: -9.99 vira -9
10                 « Math.round arredonda de verdade
9
-56                « (byte) 200: 200 não cabe num byte e "deu a volta"
4464               « (short) 70000: também deu a volta
2147483647         « (int) 30 bilhões: decimal grande demais vira o MAX_VALUE
30000000000        « num long, cabe
65
B
3.5                « (double) 7 / 2: converte o 7, depois divide
3.0                « (double) (7 / 2): divide (dá 3), depois converte

Vamos por partes, porque cada linha ensina uma regra:

CastingResultadoRegra
(int) 9.999De decimal para inteiro, a parte decimal é descartada (truncada). Nunca arredonda.
(int) -9.99-9O corte vai sempre "em direção ao zero".
Math.round(9.99)10Para arredondar, use Math.round. Ele devolve um long; se precisar de int, faça o casting do resultado: (int) Math.round(x). Veja Math.
(byte) 200-56Entre inteiros, o valor que não cabe "dá a volta", como no overflow: 200 − 256 = −56.
(int) 3.0e102147483647De decimal para inteiro, valores grandes demais param no limite do tipo (Integer.MAX_VALUE).
(double) 7 / 23.5O casting vale só para o valor logo à direita (o 7). Com um double na conta, a divisão é decimal.
(double) (7 / 2)3.0Os parênteses fazem a divisão inteira acontecer antes: 7 / 2 dá 3, e só então vira 3.0. Tarde demais.
O casting não muda a variável original

(int) preco cria um novo valor do tipo int; a variável preco continua sendo double e continua valendo 9.99. O tipo de uma variável nunca muda.

Missão: a soma das notas é 15 e são 2 provas, ambas em variáveis int. O programa mostra Média: 7.0. Use um casting (double) no lugar certo para ele mostrar Média: 7.5.

public class Main {
    public static void main(String[] args) {
        int soma = 15;
        int provas = 2;
        double media = soma / provas;
        System.out.println("Média: " + media);
    }
}
public class Main {
    public static void main(String[] args) {
        int soma = 15;
        int provas = 2;
        double media = (double) soma / provas;
        System.out.println("Média: " + media);
    }
}

3. Número ↔ texto

String não é um tipo primitivo, e o casting não converte entre texto e número: (int) "42" não compila. Para isso existem métodos.

Número → texto

Há três formas, e todas dão o mesmo resultado:

int idade = 29;
String a = "" + idade;                 // concatenar com um texto vazio
String b = String.valueOf(idade);      // "valor de": funciona com qualquer tipo
String c = Integer.toString(idade);    // método da classe Integer
FormaQuando usar
"" + idadeRápido e comum, mas parece "truque". Normal quando o número já vai ser colado num texto: "Idade: " + idade (Concatenação).
String.valueOf(idade)A mais clara. Funciona com int, double, char, boolean…
Integer.toString(idade)Equivalente; existe também Double.toString, Long.toString…

Texto → número: Integer.parseInt

Isso acontece o tempo todo: tudo o que o usuário digita chega como texto, e os argumentos da linha de comando também (Argumentos da Linha de Comando). Para transformar "42" no número 42, use Integer.parseInt (parse = "analisar, interpretar"):

public class Main {
    public static void main(String[] args) {
        int idade = 29;
        String a = "" + idade;
        String b = String.valueOf(idade);
        String c = Integer.toString(idade);
        String d = String.valueOf(1.65);
        System.out.println(a + b + c);
        System.out.println(d);
        String texto = "42";
        int numero = Integer.parseInt(texto);
        System.out.println(numero + 1);
        System.out.println(texto + 1);
        double altura = Double.parseDouble("1.65");
        System.out.println(altura * 2);
        long l = Long.parseLong("8100000000");
        System.out.println(l);
        boolean ok = Boolean.parseBoolean("true");
        System.out.println(ok);
    }
}
$ javac Main.java && java Main
292929             « três textos "29" colados
1.65
43                 « numero + 1: conta de verdade
421                « texto + 1: "42" colado com "1"
3.3
8100000000
true

Compare as linhas 3 e 4 da saída: com o número, + 1 soma; com o texto, + 1 cola. É a prova de que parseInt mudou o tipo.

Texto para…MétodoExemplo
intInteger.parseInt(texto)Integer.parseInt("42") → 42
longLong.parseLong(texto)Long.parseLong("8100000000")
doubleDouble.parseDouble(texto)Double.parseDouble("1.65") → 1.65
booleanBoolean.parseBoolean(texto)"true" → true; qualquer outro texto → false

Quando o texto não é um número: NumberFormatException

O compilador não tem como saber o que vai estar dentro de uma String. Se o texto não for um número válido, o erro aparece só na execução:

public class Main {
    public static void main(String[] args) {
        String quantidade = "doze";
        int n = Integer.parseInt(quantidade);
        System.out.println(n);
    }
}
$ javac Main.java && java Main
Exception in thread "main" java.lang.NumberFormatException: For input string: "doze"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:565)
	at java.base/java.lang.Integer.parseInt(Integer.java:662)
	at Main.main(Main.java:4)

O programa compilou, começou a rodar e parou na linha 4 com uma exceção: NumberFormatException ("exceção de formato de número"), seguida do texto que causou o problema: For input string: "doze" ("para o texto de entrada "doze""). As linhas at mostram o caminho: o erro nasceu lá dentro de Integer.parseInt, chamado pela linha 4 do Main.java (a última linha at é a que interessa: a sua). O println nunca rodou.

O parseInt é exigente. Veja o que ele aceita e o que recusa (todas as recusas dão a mesma exceção, com o texto entre aspas):

TextoInteger.parseInt
"42", "-15", "+15"Aceita: 42, -15, 15
"doze"NumberFormatException: For input string: "doze"
"3.5"NumberFormatException: For input string: "3.5" (não é inteiro; use Double.parseDouble)
" 15" (com espaço)NumberFormatException: For input string: " 15" (limpe antes com texto.strip(), de Métodos de String)
"" (vazio)NumberFormatException: For input string: ""
"3000000000"Recusa: não cabe num int (use Long.parseLong)

Para o programa não parar, você pode "capturar" a exceção e mostrar uma mensagem amigável; isso é assunto de try e catch.

Missão: a idade chegou como texto e o programa mostra Ano que vem: 291. Converta idadeTexto para int com Integer.parseInt antes de somar, para mostrar Ano que vem: 30. Depois, por curiosidade, troque "29" por "vinte" e rode de novo.

public class Main {
    public static void main(String[] args) {
        String idadeTexto = "29";
        System.out.println("Ano que vem: " + idadeTexto + 1);
    }
}
public class Main {
    public static void main(String[] args) {
        String idadeTexto = "29";
        int idade = Integer.parseInt(idadeTexto);
        System.out.println("Ano que vem: " + (idade + 1));
    }
}

4. char ↔ int

Como você viu em char e boolean, um char é um código numérico. Isso cria uma armadilha: converter o caractere '7' em int dá o código dele (55), não o número 7.

public class Main {
    public static void main(String[] args) {
        char digito = '7';
        int valor = digito - '0';
        int valor2 = Character.getNumericValue(digito);
        int errado = digito;
        System.out.println(valor);
        System.out.println(valor2);
        System.out.println(errado);
        int n = 4;
        char c = (char) ('0' + n);
        System.out.println(c);
        String s = "Ana";
        char primeira = s.charAt(0);
        System.out.println(primeira);
        String deChar = String.valueOf('Z');
        System.out.println(deChar);
    }
}
$ javac Main.java && java Main
7                  « '7' - '0'
7                  « Character.getNumericValue('7')
55                 « int errado = '7': o código Unicode!
4                  « (char) ('0' + 4): número para dígito
A                  « primeiro caractere da String
Z                  « char para String
QueroUseExemplo → resultado
Código Unicode de um char(int) c (ou só atribuir a um int)(int) 'A' → 65
char de um código(char) n(char) 66 → B
Dígito '7' → número 7c - '0' ou Character.getNumericValue(c)7
Número 4 → dígito '4'(char) ('0' + n)'4'
char → StringString.valueOf(c)"Z"
String → chartexto.charAt(posição) (começa em 0)"Ana".charAt(0) → 'A'

5. Promoção em expressões: byte + byte = int

Até aqui, as conversões aconteceram ao guardar um valor. Mas elas também acontecem no meio das contas. Antes de calcular, o Java "promove" os valores segundo duas regras:

  1. byte, short e char viram int antes de qualquer conta. O processador trabalha com no mínimo 32 bits.
  2. Se os dois lados têm tipos diferentes, o menor vira o maior: int com long dá long; qualquer coisa com double dá double.

A regra 1 produz um erro que surpreende todo mundo:

public class Main {
    public static void main(String[] args) {
        byte a = 10;
        byte b = 20;
        byte soma = a + b;
    }
}
$ javac Main.java
Main.java:5: error: incompatible types: possible lossy conversion from int to byte
        byte soma = a + b;
                      ^
1 error

"Mas eu só somei dois bytes!" Sim, só que a + b é calculado como int (regra 1), e guardar um int num byte é estreitamento. Repare que o ^ aponta para o +: é a conta que tem tipo int. Há duas saídas: guardar num int ou fazer o casting do resultado inteiro, com parênteses em volta da conta:

public class Main {
    public static void main(String[] args) {
        byte a = 10;
        byte b = 20;
        byte soma = (byte) (a + b);
        int somaInt = a + b;
        System.out.println(soma);
        System.out.println(somaInt);
        short s = 1;
        char c = 'A';
        System.out.println(s + c);
        int i = 5;
        long l = 10L;
        float f = 1.5f;
        double d = 2.0;
        System.out.println(i + l);
        System.out.println(i + f);
        System.out.println(l + d);
        System.out.println(5 / 2 * 2.0);
        System.out.println(5 * 2.0 / 2);
        System.out.println(5 / 2.0 * 2);
    }
}
$ javac Main.java && java Main
30
30
66                 « short + char: os dois viram int (1 + 65)
15                 « int + long = long
6.5                « int + float = float
12.0               « long + double = double
4.0                « 5 / 2 = 2 (inteiros!), depois 2 * 2.0 = 4.0
5.0                « 5 * 2.0 = 10.0, depois 10.0 / 2 = 5.0
5.0                « 5 / 2.0 = 2.5, depois 2.5 * 2 = 5.0

As três últimas linhas mostram que a promoção acontece a cada operação, da esquerda para a direita. Em 5 / 2 * 2.0, a primeira operação (5 / 2) só tem inteiros e perde o .5 antes de o 2.0 entrar na história. Por isso, ao calcular médias e porcentagens, coloque o valor decimal (ou o casting) no começo da conta. A ordem das operações é o assunto de Precedência.

Os dois lados da contaResultado
byte, short, char, int (qualquer mistura)int
algum long (e nenhum decimal)long
algum float (e nenhum double)float
algum doubledouble
algum String com +String (concatenação)
E por que b += 1 funciona com byte?

Os operadores compostos (+=, -=…) fazem o casting de volta sozinhos. É um detalhe importante, explicado em Operadores de Atribuição.


Quando dá errado

"possible lossy conversion from double to int" (e parecidos)

O que quer dizer: você tentou guardar um valor de um tipo maior num menor sem casting. A mensagem diz de qual tipo para qual: from double to int, from long to int, from int to byte.

Como consertar: primeiro pergunte se o tipo da variável está certo (talvez ela devesse ser double). Se a perda for aceitável, use casting: (int) valor. Se a ideia era arredondar, (int) Math.round(valor).

"String cannot be converted to int" e "int cannot be converted to String"
$ javac Main.java
Main.java:4: error: incompatible types: String cannot be converted to int
        int numero = texto;
                     ^
1 error
$ javac Main.java
Main.java:4: error: incompatible types: int cannot be converted to String
        String texto = idade;
                       ^
1 error

O que quer dizer: texto e número não se convertem por atribuição nem por casting.

Como consertar: int numero = Integer.parseInt(texto); e String texto = String.valueOf(idade);.

"NumberFormatException: For input string"
$ javac Main.java && java Main
Exception in thread "main" java.lang.NumberFormatException: For input string: "3.5"
	at java.base/java.lang.NumberFormatException.forInputString(NumberFormatException.java:67)
	at java.base/java.lang.Integer.parseInt(Integer.java:565)
	at java.base/java.lang.Integer.parseInt(Integer.java:662)
	at Main.main(Main.java:3)

O que quer dizer: o texto entre aspas não é um inteiro válido. Olhe com atenção o que está entre as aspas: espaços, vírgulas, pontos e letras aparecem ali.

Como consertar: use o método certo para o formato (Double.parseDouble para "3.5"), limpe espaços com strip(), ou trate o erro com try e catch.

O resultado do casting "não faz sentido"

O que aconteceu: (byte) 200 deu -56, (int) 9.99 deu 9, (double) (7 / 2) deu 3.0. Não é erro do Java: é exatamente o que o casting promete. Ele corta, dá a volta, e só converte o valor que está à sua direita.

Como consertar: confira se o valor cabe no tipo de destino, use Math.round para arredondar e coloque o casting antes da divisão: (double) soma / n.

Missão (provoque o erro): mude o texto "150" de modo que o Integer.parseInt lance NumberFormatException. Tente algumas formas diferentes (letras, vírgula, espaço) e leia a mensagem de cada uma.

public class Main {
    public static void main(String[] args) {
        String estoqueTexto = "150";
        int estoque = Integer.parseInt(estoqueTexto);
        System.out.println("Estoque: " + estoque);
    }
}
public class Main {
    public static void main(String[] args) {
        String estoqueTexto = "150 caixas";
        int estoque = Integer.parseInt(estoqueTexto);
        System.out.println("Estoque: " + estoque);
    }
}

Referência

ConversãoComoExemplo → resultado
Menor → maiorAutomáticadouble d = 32; → 32.0
Maior → menor(tipo) valor(int) 9.99 → 9
Decimal → inteiro arredondadoMath.round(x) (devolve long)Math.round(9.99) → 10
Número → StringString.valueOf(n), "" + n, Integer.toString(n)"29"
String → intInteger.parseInt(s)Integer.parseInt("42") → 42
String → doubleDouble.parseDouble(s)Double.parseDouble("1.65") → 1.65
String → longLong.parseLong(s)
char → código(int) c(int) 'A' → 65
Dígito → númeroc - '0', Character.getNumericValue(c)'7' → 7
boolean ↔ númeroNão existe; use b ? 1 : 0 ou n != 0

Exercícios

1. Casting

Complete para guardar a parte inteira de altura (um double):

int metros = altura;

2. Texto para número

Complete para transformar o texto "35" em int:

int idade = ("35");

3. Número para texto

Complete com o método que transforma qualquer valor em String:

String texto = String.(42);

4. O que System.out.println((int) 7.9); mostra?

5. Por que byte soma = a + b; (com a e b do tipo byte) não compila?

6. O que acontece com Integer.parseInt("12,5")?

7. Qual destas expressões dá 3.5?


Resumo

  • Alargamento (menor → maior: byte → short → int → long → float → double, e char → int) é automático.
  • Estreitamento exige casting, (int) valor, e pode perder informação: corta decimais ((int) 9.99 = 9) ou dá a volta ((byte) 200 = -56). Para arredondar, Math.round.
  • O casting vale para o valor logo à direita: (double) 7 / 2 = 3.5, mas (double) (7 / 2) = 3.0.
  • Texto → número: Integer.parseInt, Double.parseDouble; texto inválido lança NumberFormatException na execução. Número → texto: String.valueOf.
  • '7' vale 55 como int; para o número 7, use '7' - '0'.
  • Nas contas, byte, short e char viram int, e o tipo menor vira o maior: byte + byte é int.