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 levelComo 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 UserOs 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 9Ele foi descontinuado por duas razões reais:
- Chama apenas o construtor sem argumentos. Não há como passar argumentos.
- 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.
O que observar na execução:
- A fábrica genérica
buildcriouWidgeteHiddena partir de umaClassmais um array de tipos de parâmetros — sem nomear nenhum tipo em uma expressãonew. 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 oWidgetpadrão, demonstrando o substituto moderno paraClass.newInstance(). Sempre prefira-o: ele permite escolher o construtor e encaminha as exceções do construtor porInvocationTargetExceptionem vez de vazar exceções verificadas não declaradas.- A instância reflexiva de
Hiddennão era o mesmo objeto queHidden.INSTANCE(same instance? false).setAccessible(true)passou direto pelo construtorprivatee 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
IllegalArgumentExceptiondo seu corpo, e isso surgiu comoInvocationTargetExceptioncom a causa real dentro — encapsulamento idêntico ao deMethod.invoke. A validação em tempo de construção é preservada por reflexão; você só precisa desencapsular para vê-la. Constructor<T>retornou umTtipado (Widget,Hidden) sem cast, ao contrário doObjectbruto deMethod.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.