Classe imutável?

Respostas:

135

O que é um objeto imutável?

Um objeto imutável é aquele que não muda de estado após ser instanciado.

Como tornar um objeto imutável?

Em geral, um objeto imutável pode ser feito definindo uma classe que não tem nenhum de seus membros expostos e não tem nenhum setter.

A seguinte classe criará um objeto imutável:

class ImmutableInt {
  private final int value;

  public ImmutableInt(int i) {
    value = i;
  }

  public int getValue() {
    return value;
  }
}

Como pode ser visto no exemplo acima, o valor de ImmutableIntsó pode ser definido quando o objeto é instanciado e, por ter apenas um getter ( getValue), o estado do objeto não pode ser alterado após a instanciação.

No entanto, deve-se ter cuidado para que todos os objetos que são referenciados pelo objeto também sejam imutáveis, ou pode ser possível alterar o estado do objeto.

Por exemplo, permitir uma referência a uma matriz ou ArrayListser obtida por meio de um getter permitirá que o estado interno mude, alterando a matriz ou coleção:

class NotQuiteImmutableList<T> {
  private final List<T> list;

  public NotQuiteImmutableList(List<T> list) {
    // creates a new ArrayList and keeps a reference to it.
    this.list = new ArrayList(list); 
  }

  public List<T> getList() {
    return list;
  }
}

O problema com o código acima é que o ArrayListpode ser obtido getListe manipulado, fazendo com que o estado do próprio objeto seja alterado, portanto, não imutável.

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));

// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

Uma maneira de contornar esse problema é retornar uma cópia de uma matriz ou coleção quando chamada de um getter:

public List<T> getList() {
  // return a copy of the list so the internal state cannot be altered
  return new ArrayList(list);
}

Qual é a vantagem da imutabilidade?

A vantagem da imutabilidade vem com a simultaneidade. É difícil manter a correção em objetos mutáveis, pois vários threads podem estar tentando alterar o estado do mesmo objeto, levando a alguns threads vendo um estado diferente do mesmo objeto, dependendo do tempo de leitura e gravação no referido objeto.

Por ter um objeto imutável, pode-se garantir que todos os threads que estão olhando para o objeto verão o mesmo estado, pois o estado de um objeto imutável não mudará.

coobird
fonte
5
A segurança da thread (importante para a simultaneidade) não é a única vantagem da imutabilidade; também significa que você não precisa fazer cópias defensivas de objetos e evita bugs, porque você não pode modificar por engano objetos que não deveriam ser modificados.
Jesper
7
Em vez de retornar uma cópia da lista, você também pode return Collections.unmodifiableList(list);retornar uma exibição somente leitura na lista.
Jesper
18
A aula tem que ser feita finaltambém. Caso contrário, ele pode ser estendido com um método setter (ou outros tipos de métodos mutantes e campos mutáveis).
Abhinav Sarkar
6
@AbhinavSarkar Não precisa ser finalse a classe tiver apenas privatecampos, pois eles não podem ser acessados ​​a partir de subclasses.
icza
3
A classe @icza tem que ser final, porque temos o método getter público, podemos estender a classe e sobrescrever o método getter e, em seguida, alterar o campo do nosso jeito ... então ele não é mais imutável
sunil
19

Além das respostas já fornecidas, eu recomendo ler sobre imutabilidade em Effective Java, 2ª Ed., Pois existem alguns detalhes que são fáceis de ignorar (por exemplo, cópias defensivas). Além disso, Effective Java 2ª Ed. é uma leitura obrigatória para todo desenvolvedor Java.

Fabian Steeg
fonte
3
Este é o recurso exato a ser examinado. Os benefícios mencionados variam desde código menos sujeito a erros até segurança de thread.
gpampara
6

Você torna uma classe imutável assim:

public final class Immutable
{
    private final String name;

    public Immutable(String name) 
    {
        this.name = name;
    }

    public String getName() { return this.name; } 

    // No setter;
}

A seguir estão os requisitos para tornar uma classe Java imutável:

  • A classe deve ser declarada como final(para que as classes filhas não possam ser criadas)
  • Os membros da classe devem ser declarados como final(para que não possamos alterar seu valor após a criação do objeto)
  • Escreva métodos Getter para todas as variáveis ​​nele para obter valores de Membros
  • Métodos não setters

Classes imutáveis ​​são úteis porque
- Elas são seguras com thread.
- Eles também expressam algo profundo sobre o seu design: "Não é possível mudar isso.", Quando se aplica, é exatamente o que você precisa.

duffymo
fonte
5

A imutabilidade pode ser alcançada principalmente de duas maneiras:

  • usando finalatributos de instância para evitar reatribuição
  • usando uma interface de classe que simplesmente não permite nenhuma operação capaz de modificar o que está dentro de sua classe (apenas getters e não setters

As vantagens da imutabilidade são as suposições que você pode fazer sobre estes objetos:

  • você ganha a regra sem efeito colateral (que é muito popular em linguagens de programação funcional) e permite que você use objetos em um ambiente concorrente mais facilmente, pois você sabe que eles não podem ser alterados de forma atômica ou não atômica quando são usado por muitos tópicos
  • implementações de linguagens podem tratar esses objetos de uma maneira diferente, colocando-os em zonas de memória que são usadas para dados estáticos, permitindo o uso mais rápido e seguro desses objetos (é o que acontece dentro da JVM para strings)
Jack
fonte
Imutável não é o mesmo que igual sem efeitos colaterais. Por exemplo, um objeto imutável pode produzir efeitos colaterais, como registrar em um arquivo. É um pouco impreciso dizer que tornar um objeto imutável também o torna livre de efeitos colaterais.
Grundlefleck
1
@Grundleflek, acho que isso pode ser uma confusão. A classe não é imutável se modificar um arquivo de log como parte de seu contrato e esse arquivo de log estiver acessível a outras classes. Se o arquivo de log estiver oculto de outras classes e não fizer parte do contrato da classe, a classe é efetivamente imutável e, na verdade, para todos os efeitos, sem efeitos colaterais. A introdução (sem fontes) na página de efeitos colaterais da Wikipedia diz "... diz-se que uma expressão tem um efeito colateral se, além de produzir um valor, também modifica algum estado ou tem uma interação observável com funções de chamada."
Jeff Axelrod
3

Classes imutáveis ​​não podem reatribuir valores após serem instanciadas. O construtor atribui valores a suas variáveis ​​privadas. Até que o objeto se torne nulo, os valores não podem ser alterados devido à indisponibilidade dos métodos setter.

ser imutável deve satisfazer o seguinte,

  • Todas as variáveis ​​devem ser privadas .
  • Nenhum método modificador (setters) é fornecido.
  • Evite a substituição de métodos tornando a classe final (Imutabilidade forte) ou métodos finais (imutabilidade semanal).
  • Clone profundamente se contiver classes não primitivas ou mutáveis.

/**
* Strong immutability - by making class final
*/
public final class TestImmutablity {

// make the variables private
private String Name;

//assign value when the object created
public TestImmutablity(String name) {
this.Name = name;
}

//provide getters to access values
public String getName() {

return this.Name;
}
}

Vantagens: Objetos imutáveis ​​contêm seus valores inicializados até que morram.

java-immutable-classes-short-note

Kandy
fonte
2

Classe imutável são aquelas cujos objetos não podem ser alterados após a criação.

Classes imutáveis ​​são úteis para

  • Finalidade de cache
  • Ambiente simultâneo (ThreadSafe)
  • Difícil para herança
  • O valor não pode ser alterado em nenhum ambiente

Exemplo

Classe String

Exemplo de Código

public final class Student {
    private final String name;
    private final String rollNumber;

    public Student(String name, String rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public String getName() {
        return this.name;
    }

    public String getRollNumber() {
        return this.rollNumber;
    }
}
Yasir Shabbir Choudhary
fonte
2

Como tornar uma classe Java imutável?

Do JDK 14+ que tem JEP 359 , podemos usar "records ". É a maneira mais simples e descomplicada de criar uma classe Imutável.

Uma classe de registro é um portador transparente e superficialmente imutável para um conjunto fixo de campos conhecido como registro, componentsque fornece uma statedescrição para o registro. Cada um componentdá origem a um finalcampo que contém o valor fornecido e um accessormétodo para recuperar o valor. O nome do campo e o nome do acessador correspondem ao nome do componente.

Vamos considerar o exemplo de criação de um retângulo imutável

record Rectangle(double length, double width) {}

Não há necessidade de declarar nenhum construtor, nem de implementar métodos equals & hashCode. Qualquer registro precisa de um nome e uma descrição de estado.

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

Se você quiser validar o valor durante a criação do objeto, temos que declarar explicitamente o construtor.

public Rectangle {

    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

O corpo do registro pode declarar métodos estáticos, campos estáticos, inicializadores estáticos, construtores, métodos de instância e tipos aninhados.

Métodos de Instância

record Rectangle(double length, double width) {

  public double area() {
    return this.length * this.width;
  }
}

campos estáticos, métodos

Como o estado deve fazer parte dos componentes, não podemos adicionar campos de instância aos registros. Mas, podemos adicionar métodos e campos estáticos:

record Rectangle(double length, double width) {

  static double aStaticField;

  static void aStaticMethod() {
    System.out.println("Hello Static");
  }
}

qual é a necessidade de imutabilidade e há alguma vantagem em usar isso?

Respostas postadas anteriormente são boas o suficiente para justificar a necessidade de imutabilidade e seus prós

Bhanu Hoysala
fonte
0

Outra maneira de tornar o objeto imutável é usando a biblioteca Immutables.org :

Supondo que as dependências necessárias foram adicionadas, crie uma classe abstrata com métodos de acesso abstratos. Você pode fazer o mesmo anotando com interfaces ou mesmo anotações (@interface):

package info.sample;

import java.util.List;
import java.util.Set;
import org.immutables.value.Value;

@Value.Immutable
public abstract class FoobarValue {
  public abstract int foo();
  public abstract String bar();
  public abstract List<Integer> buz();
  public abstract Set<Long> crux();
}

Agora é possível gerar e usar a implementação imutável gerada:

package info.sample;

import java.util.List;

public class FoobarValueMain {
  public static void main(String... args) {
    FoobarValue value = ImmutableFoobarValue.builder()
        .foo(2)
        .bar("Bar")
        .addBuz(1, 3, 4)
        .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}

    int foo = value.foo(); // 2

    List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
  }
}
Assim como
fonte
0

Uma classe imutável é simplesmente uma classe cujas instâncias não podem ser modificadas.

Todas as informações contidas em cada instância são fixas para o tempo de vida do objeto, portanto, nenhuma mudança pode ser observada.

Classes imutáveis ​​são mais fáceis de projetar, implementar e usar do que classes mutáveis.

Para tornar uma classe imutável, siga estas cinco regras:

  1. Não forneça métodos que modifiquem o estado do objeto

  2. Certifique-se de que a aula não pode ser estendida.

  3. Faça todos os campos finais.

  4. Torne todos os campos privados.

  5. Garanta acesso exclusivo a quaisquer componentes mutáveis.

Objetos imutáveis ​​são inerentemente thread-safe; eles não requerem sincronização.

Objetos imutáveis ​​podem ser compartilhados livremente.

Objetos imutáveis ​​são ótimos blocos de construção para outros objetos

Sandeep Vanama
fonte
0

@Jack, Ter campos finais e setters em classe não tornará a classe imutável. a palavra-chave final apenas garante que uma variável nunca seja reatribuída. Você precisa retornar uma cópia profunda de todos os campos nos métodos getter. Isso garantirá que, após obter um objeto do método getter, o estado interno do objeto não seja perturbado.

Amit Toor
fonte
-1

Como um falante não nativo de inglês, não gosto da interpretação comum de "classe imutável" sendo "objetos de classe construídos são imutáveis"; antes, eu mesmo me inclino a interpretar isso como "o próprio objeto de classe é imutável".

Dito isso, "classe imutável" é um tipo de objeto imutável. A diferença está em responder qual é o benefício. Pelo meu conhecimento / interpretação, a classe imutável impede que seus objetos modifiquem o comportamento em tempo de execução.

bbgw
fonte
-1

A maioria das respostas aqui são boas, e algumas mencionaram as regras, mas acho bom colocar em palavras por que e quando precisamos seguir essas regras. Então estou dando a explicação abaixo

  • Declare as variáveis ​​de membro como 'finais' - Quando as declaramos como finais, o compilador nos força a inicializá-las. Podemos inicializar diretamente, pelo construtor padrão, pelo construtor arg. (Veja o código de exemplo abaixo) e após a inicialização não podemos modificá-los porque são finais.
  • E, claro, se tentarmos usar Setters para essas variáveis ​​finais, o compilador gerará erro.

    public class ImmutableClassExplored {
    
        public final int a; 
        public final int b;
    
        /* OR  
        Generally we declare all properties as private, but declaring them as public 
        will not cause any issues in our scenario if we make them final     
        public final int a = 109;
        public final int b = 189;
    
         */
        ImmutableClassExplored(){
            this. a = 111;
            this.b = 222;
        }
    
        ImmutableClassExplored(int a, int b){
            this.a = a;
            this.b= b;
        }
    }
    

Precisamos declarar a classe como 'final'?

  • Sem a palavra-chave final na declaração da classe, a classe pode ser herdada. Portanto, a subclasse pode substituir os métodos getter. Aqui temos que considerar dois cenários:

1. Tendo apenas membros primitivos: Não temos problemas Se a classe tiver apenas membros primitivos, então não precisamos declarar a classe como final.

2. Tendo objetos como variáveis ​​de membro: Se tivermos objetos como variáveis ​​de membro, então temos que tornar os membros desses objetos também finais. Significa que precisamos atravessar profundamente a árvore e tornar todos os objetos / primitivos finais, o que pode não ser possível o tempo todo. Portanto, a solução alternativa é tornar a classe final, o que evita a herança. Portanto, não há dúvida de que métodos getter sobrescrevem subclasses.

Srikant M
fonte
-1

A anotação @Value do Lombok pode ser usada para gerar classes imutáveis. É tão simples quanto o código abaixo.

@Value
public class LombokImmutable {
    int id;
    String name;
}

De acordo com a documentação no site do Lombok:

@Value é a variante imutável de @Data; todos os campos se tornam privados e finais por padrão, e os setters não são gerados. A própria classe também é finalizada por padrão, porque a imutabilidade não é algo que pode ser forçado em uma subclasse. Como @Data, os métodos úteis toString (), equals () e hashCode () também são gerados, cada campo obtém um método getter e um construtor que cobre todos os argumentos (exceto campos finais que são inicializados na declaração de campo) também é gerado .

Um exemplo totalmente funcional pode ser encontrado aqui.

Anubhav
fonte
Antes de responder a uma pergunta antiga com uma resposta aceita (procure o ✓ verde), bem como outras respostas, certifique-se de que sua resposta acrescente algo novo ou seja útil em relação a elas. Por favor, tome cuidado ao responder à pergunta do OP Como alguém pode tornar uma classe Java imutável, qual é a necessidade de imutabilidade e há alguma vantagem em usá-la? . - Você está dando apenas uma resposta parcial que afirma apenas que se pode usar o Lombok, uma estrutura / biblioteca de terceiros, que não é necessariamente sobre o que a pergunta do OP se trata. Também o Java 15 está fora, por que usar o Lombok quando o Java recordpode ser usado?
Ivo Mori
Estou dando uma das opções de como conseguir isso. Nem todo mundo está usando o Java 15, a maioria dos aplicativos hoje são executados em versões anteriores, então você dizer por que usar o Lombok não faz muito sentido. Em meu projeto atual, migramos recentemente para o Java 11 e estamos usando o Lombok para obter classes imutáveis. Além disso, minha resposta é um acréscimo às respostas já presentes. O que é imutável já foi respondido, acho que você espera que eu reescreva isso apenas para completar.
Anubhav
Justo. Eu não votei contra, nem votei. Tentei apontar os motivos pelos quais duas outras pessoas votaram contra essa resposta. Depende de você se e como deseja editar sua resposta.
Ivo Mori