W3docs

Java Reflection: Chamando Construtores

Instancie classes Java por reflexão com Constructor.newInstance e Class.getDeclaredConstructor.

Criar um objeto sem escrever new é o truque reflexivo por trás de todo container de injeção de dependência, deserializador e carregador de plugins: você tem uma Class e precisa de uma instância. O objeto Constructor<T> representa um construtor e cria instâncias com newInstance(args...). Este capítulo cobre como encontrar construtores, chamá-los com argumentos, acessar construtores private e por que o atalho antigo Class.newInstance() foi descontinuado.

Se você é novo em reflection, comece pela introdução à reflection e depois volte aqui. Os mecanismos abaixo espelham o que você viu para chamar métodos e ler campos por reflexão.

Encontrando construtores

Os construtores são pesquisados apenas pelos tipos dos parâmetros — não há nome, pois todos os construtores compartilham o nome da classe:

Class<User> c = User.class;

Constructor<User> noArg  = c.getDeclaredConstructor();                  // ()
Constructor<User> twoArg = c.getDeclaredConstructor(String.class, int.class);  // (String, int)

Constructor<?>[] pub  = c.getConstructors();          // public only
Constructor<?>[] all  = c.getDeclaredConstructors();  // any access level

Como em toda parte na reflection, os tipos dos parâmetros devem coincidir exatamente (int.class, não Integer.class), e getConstructor vê apenas os public, enquanto getDeclaredConstructor vê também private/protected/pacote. Note que Constructor<T> é genérico na classe que constrói, então newInstance retorna um T tipado (ao contrário do Object bruto de Method.invoke).

Criando instâncias com newInstance

Constructor<User> ctor = User.class.getDeclaredConstructor(String.class, int.class);
User u = ctor.newInstance("ada", 36);     // returns a typed User

Os argumentos funcionam como em Method.invoke: um varargs Object[], com primitivos autoboxed. A diferença é que não há objeto alvo — um construtor cria o alvo. As exceções lançadas pelo corpo do construtor são encapsuladas em InvocationTargetException, exatamente como acontece com métodos; desencapsule com getCause().

Acessando construtores privados

Singletons, classes utilitárias e builders frequentemente ocultam seus construtores. A reflection contorna isso com setAccessible(true):

Constructor<Singleton> ctor = Singleton.class.getDeclaredConstructor();
ctor.setAccessible(true);                 // bypass the private modifier
Singleton fresh = ctor.newInstance();     // a SECOND instance — breaks the singleton!

Isso é genuinamente poderoso e genuinamente perigoso: derrota a garantia do singleton, o contrato de "sem instâncias" de uma classe utilitária e qualquer invariante que o construtor estava protegendo. (Um singleton com enum é a única forma que a reflection não consegue instanciar — Constructor.newInstance recusa explicitamente tipos enum com IllegalArgumentException, o que é parte do motivo pelo qual "singleton com enum" é o padrão recomendado.)

Por que Class.newInstance() foi descontinuado

Você verá código antigo usar o atalho clazz.newInstance():

User u = User.class.newInstance();   // DEPRECATED since Java 9

Ele foi descontinuado por duas razões reais:

  1. Chama apenas o construtor sem argumentos. Não há como passar argumentos.
  2. Trata exceções de forma incorreta. Se o construtor sem argumentos lançar uma exceção verificada, Class.newInstance() a propagará sem declará-la — anulando a análise de exceções verificadas do compilador.

O substituto é sempre:

User u = User.class.getDeclaredConstructor().newInstance();

É uma linha mais longa, chama um construtor que você escolheu explicitamente e encapsula as exceções do construtor em InvocationTargetException para que nada escape sem declaração. Use isso como o idioma padrão mesmo para o caso sem argumentos.

Um exemplo prático: uma pequena fábrica reflexiva

O programa constrói objetos de três formas: um construtor público com vários argumentos, um construtor private acessado via setAccessible e o idioma moderno sem argumentos — depois mostra um construtor que lança exceção sendo encapsulado e a assinatura do atalho descontinuado para comparação.

java— editable, runs on the server

O que observar na execução:

  • A fábrica genérica build criou Widget e Hidden a partir de uma Class mais um array de tipos de parâmetros — sem nomear nenhum tipo em uma expressão new. Essa assinatura, <T> T build(Class<T>, Class<?>[], Object...), é essencialmente como o núcleo de instanciação de um container DI se parece: passe um tipo e argumentos, receba uma instância de volta.
  • getDeclaredConstructor().newInstance() produziu o Widget padrão, demonstrando o substituto moderno para Class.newInstance(). Sempre prefira-o: ele permite escolher o construtor e encaminha as exceções do construtor por InvocationTargetException em vez de vazar exceções verificadas não declaradas.
  • A instância reflexiva de Hidden não era o mesmo objeto que Hidden.INSTANCE (same instance? false). setAccessible(true) passou direto pelo construtor private e criou uma segunda instância — prova concreta de que a reflection pode quebrar a garantia central de um singleton. Singletons defensivos lançam uma exceção no construtor se uma instância já existir; enums são imunes por construção.
  • O construtor que rejeitou um tamanho negativo lançou IllegalArgumentException do seu corpo, e isso surgiu como InvocationTargetException com a causa real dentro — encapsulamento idêntico ao de Method.invoke. A validação em tempo de construção é preservada por reflexão; você só precisa desencapsular para vê-la.
  • Constructor<T> retornou um T tipado (Widget, Hidden) sem cast, ao contrário do Object bruto de Method.invoke. Como o construtor é genérico na classe que constrói, a fábrica permanece type-safe em sua fronteira mesmo que tudo dentro seja reflexivo.

Prática

Prática
Um colega escreve 'MyService s = MyService.class.newInstance();' e o linter sinaliza 'newInstance()' como descontinuado. Qual é o substituto recomendado e qual é a principal razão prática pela qual a forma antiga foi descontinuada?
Um colega escreve 'MyService s = MyService.class.newInstance();' e o linter sinaliza 'newInstance()' como descontinuado. Qual é o substituto recomendado e qual é a principal razão prática pela qual a forma antiga foi descontinuada?
Was this page helpful?