Instanciar uma classe interna
-
05-09-2019 - |
Pergunta
Eu tenho um método de utilitário e quando a lógica irrelevante é removido a partir dele, o método simplificado ficaria assim:
public static <A extends Foo> List<A> getFooList(Class<A> clazz) {
List<A> returnValue = new ArrayList<A>();
for(int i=0; i < 5; i++) {
A object = clazz.newInstance();
returnValue.add(object);
}
return returnValue;
}
O problema é que, se clazz
é uma classe interna, como Foo.Bar.class
, então o método newInstance()
não vai funcionar mesmo se Bar
seria pública, uma vez que irá lançar um java.lang.InstantiationException
.
Existe uma maneira de classes internas dinamicamente instanciar?
Solução
Se é realmente um interior classe em vez de uma classe aninhada (estático), há um parâmetro de construtor implícito, que é a referência para a instância da classe externa. Você não pode usar Class.newInstance
nessa fase - você tem que obter o construtor apropriado. Aqui está um exemplo:
import java.lang.reflect.*;
class Test
{
public static void main(String[] args) throws Exception
{
Class<Outer.Inner> clazz = Outer.Inner.class;
Constructor<Outer.Inner> ctor = clazz.getConstructor(Outer.class);
Outer outer = new Outer();
Outer.Inner instance = ctor.newInstance(outer);
}
}
class Outer
{
class Inner
{
// getConstructor only returns a public constructor. If you need
// non-public ones, use getDeclaredConstructors
public Inner() {}
}
}
Outras dicas
Algo mais genérico:
public static <T> T createInstance(final Class<T> clazz) throws SecurityException, NoSuchMethodException,
IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
T instanceToReturn = null;
Class< ? > enclosingClass = clazz.getEnclosingClass();
if (enclosingClass != null) {
Object instanceOfEnclosingClass = createInstance(enclosingClass);
Constructor<T> ctor = clazz.getConstructor(enclosingClass);
if (ctor != null) {
instanceToReturn = ctor.newInstance(instanceOfEnclosingClass);
}
} else {
instanceToReturn = clazz.newInstance();
}
return instanceToReturn;
}
Esta exceção será lançada somente se clazz representa ou uma classe abstrata ou uma interface. Tem certeza de que você está passando um objeto de classe que representa uma classe concreta?
Em JMockit 1,41, use o seguinte:
ConstructorReflection.newInstance