Recentemente, você investigou o padrão de design singleton , como implementá-lo em Java e para que serve. Mas e se eu disser que o Java vem com seu próprio singleton pronto para uso? Intrigado? Então vamos mergulhar.

Você provavelmente já conhece a classe Enum . Ele tem uma característica especial que você deve conhecer. Especificamente, Enum implementa o padrão de design singleton. Essa opção é quase a mesma que a abordagem singleton envolvendo um campo público .

Singleton como enum:


public enum Device {   
    PRINTER	
} 
    

Singleton como uma variável pública:


public class Printer {   
    public static final Printer PRINTER = new Printer();   
    private Printer() {
    }
//…
}
    

A abordagem enum é mais compacta do que a abordagem de campo público, pois não precisamos escrever nossa própria implementação. Mais importante ainda, os enums não têm problemas com a serialização.

A serialização de enums funciona de maneira diferente do que para objetos comuns: apenas o valor do nome do enum é serializado. Durante a desserialização, o método é usado com o nome desserializado para obter uma instância. Além disso, o enum pode protegê-lo contra ataques de reflexão .

Você aprenderá mais sobre reflexão nas lições do segundo módulo, onde exploraremos a API de reflexão .

Java proíbe a instanciação de enums — uma limitação embutida na implementação do método newInstance da classe Constructor , que geralmente é chamado ao criar objetos por meio de reflexão.

Trecho do código de Constructor.newInstance . Usado para criar uma enumeração :


if ((clazz.getModifiers() & Modifier.ENUM) != 0)
    throw new IllegalArgumentException("Cannot reflectively create enum objects");
    

As desvantagens de usar um enum para criar um singleton incluem:

  • Falta de inicialização preguiçosa, pois o objeto é criado imediatamente e a inicialização não pode ser atrasada.

  • Outras classes não podem ser estendidas. Ou seja, nos casos em que você precisa herdar outra classe, não vai adiantar usar um enum como singleton. Nesses casos, precisamos recorrer às outras opções de implementação que já conhecemos: um método estático ou uma variável pública.

  • Ao usar enum como singleton, você pode usar apenas um campo enum .


public enum Device extends Electricity { 
    PRINTER 
}
    

Este código nos dará um erro de compilação:

Nenhuma cláusula extends permitida para enum

Mas se precisarmos implementar uma interface, não há problema, pois enum pode implementar interfaces:


public enum Device implements Electricity { 
    PRINTER 
}
    

Se você não precisa usar herança, é melhor implementar o padrão singleton via enum . Não estamos sozinhos em recomendar isso - o próprio Joshua Bloch também .

Essa abordagem de implementação oferece conveniência, compacidade, serialização pronta para uso, proteção contra ataques de reflexão e exclusividade — tudo o que um bom singleton precisa!