Java Operadores de Atribuição

O operador de atribuição = guarda um valor numa variável; você o usa desde a aula de Variáveis. Mas muitas vezes queremos atualizar o valor que já está lá: somar ao saldo, descontar do estoque, dobrar a pontuação. Para isso o Java tem os operadores compostos, como +=. Eles são atalhos, mas escondem um detalhe importante que esta aula mostra com cuidado.

Você vai aprender
  • Como o = funciona por dentro (direita primeiro, depois guarda).
  • Os operadores +=, -=, *=, /= e %=, inclusive com String.
  • O casting escondido dos compostos: por que b += 1 compila com byte e b = b + 1 não.
  • As armadilhas =+ e if (x = 5).

Relembrando o =

O = é uma ordem: "calcule o lado direito e guarde o resultado na variável do lado esquerdo". Leia sempre como "recebe".

saldo
A variável que vai receber o valor. Precisa ser uma variável: não dá para escrever 10 = saldo.
=
Recebe.
saldo + 50
A expressão, calculada primeiro, com o valor atual de saldo.
;
Fim da instrução.

Em saldo = saldo + 50;, o Java lê o saldo atual, soma 50 e guarda o resultado de volta em saldo. O valor antigo é substituído. Esse padrão, "pegar o valor atual, fazer uma conta e guardar de volta", é tão comum que ganhou atalhos.


Os operadores compostos

Um operador composto junta uma operação aritmética com o =. saldo += 50 quer dizer exatamente "saldo recebe saldo mais 50". Imagine a conta bancária da Ana ao longo de um dia:

public class Main {
    public static void main(String[] args) {
        double saldo = 100.0;
        System.out.println(saldo);
        saldo += 50;
        System.out.println(saldo);
        saldo -= 30;
        System.out.println(saldo);
        saldo *= 2;
        System.out.println(saldo);
        saldo /= 4;
        System.out.println(saldo);
        int pontos = 17;
        pontos %= 5;
        System.out.println(pontos);
    }
}
$ javac Main.java && java Main
100.0
150.0              « += 50: depósito
120.0              « -= 30: saque
240.0              « *= 2: dobrou
60.0               « /= 4: dividiu por 4
2                  « 17 %= 5: ficou o resto, 2
OperadorExemploEquivale aLê-se
=x = 5—x recebe 5
+=x += 5x = x + 5some 5 a x
-=x -= 5x = x - 5subtraia 5 de x
*=x *= 5x = x * 5multiplique x por 5
/=x /= 5x = x / 5divida x por 5
%=x %= 5x = x % 5x fica com o resto da divisão por 5

As mesmas regras dos Operadores Aritméticos valem aqui: com int, n /= 4 faz divisão inteira (6 /= 4 deixa 1), e /= 0 lança ArithmeticException. Para somar ou subtrair exatamente 1, prefira x++ e x--.

O lado direito é calculado inteiro, antes

Em x *= 2 + 3, o Java calcula todo o lado direito (2 + 3 = 5) e só depois multiplica: o resultado é x = x * (2 + 3), e não x = x * 2 + 3. Com x valendo 10, dá 50, como mostra o próximo programa.

Com String: += acrescenta texto

Como o + cola textos, o += acrescenta texto ao final de uma String. Os outros compostos não funcionam com texto.

public class Main {
    public static void main(String[] args) {
        String mensagem = "Olá";
        mensagem += ", Ana";
        mensagem += "!";
        System.out.println(mensagem);
        int total = 10;
        total += 2 * 3;
        System.out.println(total);
        int x = 10;
        x *= 2 + 3;
        System.out.println(x);
        int a, b, c;
        a = b = c = 7;
        System.out.println(a + " " + b + " " + c);
    }
}
$ javac Main.java && java Main
Olá, Ana!
16                 « total += 2 * 3: 10 + 6
50                 « x *= 2 + 3: 10 * 5, não 10 * 2 + 3
7 7 7              « atribuição em cadeia

A última linha mostra a atribuição em cadeia: a = b = c = 7 é resolvido da direita para a esquerda. Primeiro c recebe 7; o resultado dessa atribuição (o próprio 7) vai para b, e depois para a. Isso funciona porque, em Java, uma atribuição também produz um valor: o valor guardado. É por isso que System.out.println(x += 5); compila e mostra o novo valor de x. Funciona, mas evite: atribuir e usar na mesma linha confunde quem lê.

Missão: o carrinho da Ana tem três produtos. Use += para somar cada preço ao total (uma linha por produto), até o programa mostrar Total: 37.5.

public class Main {
    public static void main(String[] args) {
        double total = 0;
        double arroz = 22.90;
        double feijao = 8.60;
        double cafe = 6.00;
        // some os três preços ao total com +=

        System.out.println("Total: " + total);
    }
}
public class Main {
    public static void main(String[] args) {
        double total = 0;
        double arroz = 22.90;
        double feijao = 8.60;
        double cafe = 6.00;
        total += arroz;
        total += feijao;
        total += cafe;
        System.out.println("Total: " + total);
    }
}

O casting escondido do +=

Até aqui, x += 5 e x = x + 5 pareceram idênticos. Quase. Veja com uma variável byte:

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

A linha 4 (b += 1) passou; a linha 6 (b = b + 1) não. Por quê?

  • b + 1 é uma conta, e contas com byte são feitas em int (a promoção de Conversão de Tipos). Guardar um int num byte exige casting. Daí o erro.
  • Os operadores compostos têm um casting embutido. Pela especificação do Java, b += 1 equivale a b = (byte) (b + 1): o resultado é convertido de volta para o tipo da variável, sem você pedir.

Isso é conveniente com byte, short e char (você já viu letra += 2 avançar letras em char e boolean). Mas o mesmo casting escondido também corta valores sem avisar:

public class Main {
    public static void main(String[] args) {
        int estoque = 10;
        estoque += 3.7;
        System.out.println(estoque);
        int preco = 10;
        preco *= 1.5;
        System.out.println(preco);
        byte b = 120;
        b += 10;
        System.out.println(b);
        char letra = 'A';
        letra += 2;
        System.out.println(letra);
    }
}
$ javac Main.java && java Main
13                 « 10 + 3.7 = 13.7, cortado para 13 sem erro nenhum!
15                 « 10 * 1.5 = 15.0, que vira 15
-126               « 120 + 10 = 130 não cabe num byte: deu a volta
C                  « 'A' + 2 continua char
CódigoCompila?Por quê
estoque = estoque + 3.7; (int)Não: possible lossy conversion from double to intSem casting automático
estoque += 3.7; (int)Sim, e guarda 13Equivale a estoque = (int) (estoque + 3.7)
b = b + 1; (byte)Não: possible lossy conversion from int to byteb + 1 é int
b += 1; (byte)SimEquivale a b = (byte) (b + 1)
O += não protege você

Com =, o javac recusa conversões com perda. Com +=, *= e companhia, ele aceita e corta em silêncio. Se o valor da direita pode ter casas decimais, a variável provavelmente deveria ser double.

Missão: o nível do jogador é um byte e a linha que sobe de nível não compila. Troque-a por um operador composto, sem casting, para o programa mostrar Nível 6.

public class Main {
    public static void main(String[] args) {
        byte nivel = 5;
        nivel = nivel + 1;
        System.out.println("Nível " + nivel);
    }
}
public class Main {
    public static void main(String[] args) {
        byte nivel = 5;
        nivel += 1;
        System.out.println("Nível " + nivel);
    }
}

Quando dá errado

=+ no lugar de +=: compila e faz outra coisa
public class Main {
    public static void main(String[] args) {
        int pontos = 10;
        pontos =+ 5;
        System.out.println(pontos);
        pontos =- 3;
        System.out.println(pontos);
    }
}
$ javac Main.java && java Main
5                  « esperava 15
-3                 « esperava 2

O que aconteceu: o Java leu pontos =+ 5 como pontos = +5: atribuição comum de "mais cinco" (o + unário). E =- 3 virou = -3. Nenhum erro, nenhum aviso: só o valor errado.

Como consertar: nos compostos, o sinal da operação vem antes do =: +=, -=. Lembre: "mais-igual".

if (idade = 18): "int cannot be converted to boolean"
public class Main {
    public static void main(String[] args) {
        int idade = 18;
        if (idade = 18) {
            System.out.println("Maior de idade");
        }
    }
}
$ javac Main.java
Main.java:4: error: incompatible types: int cannot be converted to boolean
        if (idade = 18) {
                  ^
1 error

O que quer dizer: = atribui; não compara. idade = 18 guarda 18 e produz o valor 18, um int, e o if precisa de um boolean. Em C, esse erro compila e o programa faz a coisa errada; em Java, o tipo boolean pega o erro na hora (veja char e boolean).

Como consertar: para comparar, use dois sinais: if (idade == 18). Veja Operadores de Comparação.

+= numa variável sem valor: "might not have been initialized"
$ javac Main.java
Main.java:4: error: variable total might not have been initialized
        total += 5;
        ^
1 error

O que quer dizer: total += 5 precisa ler o valor atual de total, e ela foi só declarada (int total;).

Como consertar: comece de um valor: int total = 0;. Todo acumulador começa em zero (ou em "", para texto).

Espaço no meio: x + = 2 dá "illegal start of expression"
$ javac Main.java
Main.java:4: error: illegal start of expression
        x + = 2;
            ^
1 error

O que quer dizer: += é um operador de dois caracteres; com espaço no meio, viram dois símbolos soltos (+ e =) e o Java não entende.

Como consertar: escreva junto: x += 2;.

Lados trocados: 10 = estoque dá "unexpected type"
$ javac Main.java
Main.java:4: error: unexpected type
        10 = estoque;
        ^
  required: variable
  found:    value
1 error

O que quer dizer: à esquerda do = precisa haver uma variável (required: variable), e foi encontrado um valor (found: value). Não dá para guardar algo "dentro do 10".

Como consertar: a variável que recebe fica sempre à esquerda: estoque = 10;.


Referência

OperadorEquivale aExemplo (x = 10) → x
x = v—x = 3 → 3
x += vx = (tipo de x) (x + v)x += 3 → 13
x -= vx = (tipo de x) (x - v)x -= 3 → 7
x *= vx = (tipo de x) (x * v)x *= 3 → 30
x /= vx = (tipo de x) (x / v)x /= 3 → 3 (inteiro)
x %= vx = (tipo de x) (x % v)x %= 3 → 1
s += "txt"s = s + "txt"Acrescenta texto a uma String
a = b = vb = v; a = b;Da direita para a esquerda

Existem ainda os compostos de operadores de bits: &=, |=, ^=, <<=, >>= e >>>=. Eles seguem a mesma lógica (x &= y é x = x & y); o &= e o |= com boolean aparecem em Operadores Lógicos.


Exercícios

1. Depósito

Complete com o operador composto que soma 200 ao saldo:

saldo 200;

2. Metade

Complete para dividir bolo por 2, guardando o resultado nela mesma:

bolo 2;

3. Quanto vale?

Depois de int x = 4; x *= 3 + 1;, quanto vale x?

4. Com byte b = 5;, qual linha compila?

5. Depois de int n = 10; n =+ 3;, quanto vale n?

6. Depois de int estoque = 10; estoque += 2.9;, quanto vale estoque?


Resumo

  • = calcula a direita e guarda na esquerda; a esquerda precisa ser uma variável.
  • +=, -=, *=, /= e %= atualizam a variável usando o valor atual: saldo += 50 é saldo = saldo + 50. Todo o lado direito é calculado antes.
  • Com String, += acrescenta texto.
  • Os compostos têm casting embutido: byte b += 1 compila (e b = b + 1 não), mas int x += 3.7 corta para inteiro sem avisar.
  • Cuidado com =+ (vira "recebe mais") e com = no lugar de == num if.