Criar instância do tipo genérico em Java?

É possível criar uma instância de um tipo genérico em Java? Eu estou pensando com base no que eu vi que a resposta é no ( devido ao tipo de apagamento ), mas eu estaria interessado se alguém pode ver algo que eu estou faltando:

 class SomeContainer { E createContents() { return what??? } } 

EDIT: Acontece que Super Type Tokens poderia ser usado para resolver o meu problema, mas requer muito código baseado em reflection, como algumas das respostas abaixo indicaram.

Deixarei isso em aberto por um tempo para ver se alguém cria algo drasticamente diferente do artigo Artima de Ian Robertson.

Você está certo. Você não pode fazer new E() . Mas você pode mudar isso para

 private static class SomeContainer { E createContents(Class clazz) { return clazz.newInstance(); } } 

É uma dor. Mas isso funciona. Envolvê-lo no padrão de fábrica torna um pouco mais tolerável.

Não sei se isso ajuda, mas quando você subclass (incluindo anonimamente) um tipo genérico, as informações do tipo estão disponíveis por meio de reflection. por exemplo,

 public abstract class Foo { public E instance; public Foo() throws Exception { instance = ((Class)((ParameterizedType)this.getClass(). getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(); ... } } 

Então, quando você subclass Foo, você obtém uma instância de Bar, por exemplo,

 // notice that this in anonymous subclass of Foo assert( new Foo() {}.instance instanceof Bar ); 

Mas é muito trabalho e só funciona para subclasss. Pode ser útil embora.

Você precisará de algum tipo de fábrica abstrata de um tipo ou outro para passar o dinheiro para:

 interface Factory { E create(); } class SomeContainer { private final Factory factory; SomeContainer(Factory factory) { this.factory = factory; } E createContents() { return factory.create(); } } 

No Java 8, você pode usar a interface funcional Supplier para conseguir isso facilmente:

 class SomeContainer { private Supplier supplier; SomeContainer(Supplier supplier) { this.supplier = supplier; } E createContents() { return supplier.get(); } } 

Você construiria essa class assim:

 SomeContainer stringContainer = new SomeContainer<>(String::new); 

A syntax String::new nessa linha é uma referência de construtor .

Se seu construtor aceita argumentos, você pode usar uma expressão lambda:

 SomeContainer bigIntegerContainer = new SomeContainer<>(() -> new BigInteger(1)); 
 package org.foo.com; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; /** * Basically the same answer as noah's. */ public class Home { @SuppressWarnings ("unchecked") public Class getTypeParameterClass() { Type type = getClass().getGenericSuperclass(); ParameterizedType paramType = (ParameterizedType) type; return (Class) paramType.getActualTypeArguments()[0]; } private static class StringHome extends Home { } private static class StringBuilderHome extends Home { } private static class StringBufferHome extends Home { } /** * This prints "String", "StringBuilder" and "StringBuffer" */ public static void main(String[] args) throws InstantiationException, IllegalAccessException { Object object0 = new StringHome().getTypeParameterClass().newInstance(); Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance(); Object object2 = new StringBufferHome().getTypeParameterClass().newInstance(); System.out.println(object0.getClass().getSimpleName()); System.out.println(object1.getClass().getSimpleName()); System.out.println(object2.getClass().getSimpleName()); } } 

Se você precisar de uma nova instância de um argumento de tipo dentro de uma class genérica, faça seus construtores exigirem sua class …

 public final class Foo { private Class typeArgumentClass; public Foo(Class typeArgumentClass) { this.typeArgumentClass = typeArgumentClass; } public void doSomethingThatRequiresNewT() throws Exception { T myNewT = typeArgumentClass.newInstance(); ... } } 

Uso:

 Foo barFoo = new Foo(Bar.class); Foo etcFoo = new Foo(Etc.class); 

Prós:

  • Muito mais simples (e menos problemático) do que a abordagem de Super Type Token (STT) de Robertson.
  • Muito mais eficiente do que a abordagem STT (que vai comer o seu celular no café da manhã).

Contras:

  • Não é possível passar Class para um construtor padrão (é por isso que Foo é final). Se você realmente precisa de um construtor padrão, você sempre pode adicionar um método setter, mas deve se lembrar de ligar para ela mais tarde.
  • Objeção de Robertson … Mais Barras que uma ovelha negra (embora especificar a class de argumento de tipo mais uma vez não o mate exatamente). E, ao contrário das afirmações de Robertson, isso não viola o principal DRY, porque o compilador irá garantir a exatidão do tipo.
  • Não totalmente à prova de Foo . Para iniciantes … newInstance() lançará um wobbler se a class de argumento type não tiver um construtor padrão. Isso se aplica a todas as soluções conhecidas de qualquer maneira.
  • Falta o encapsulamento total da abordagem STT. Não é um grande problema embora (considerando a sobrecarga de desempenho ultrajante do STT).

Você pode fazer isso agora e não requer um monte de código de reflection.

 import com.google.common.reflect.TypeToken; public class Q26289147 { public static void main(final String[] args) throws IllegalAccessException, InstantiationException { final StrawManParameterizedClass smpc = new StrawManParameterizedClass() {}; final String string = (String) smpc.type.getRawType().newInstance(); System.out.format("string = \"%s\"",string); } static abstract class StrawManParameterizedClass { final TypeToken type = new TypeToken(getClass()) {}; } } 

É claro que se você precisar chamar o construtor que exigirá alguma reflection, mas isso é muito bem documentado, esse truque não é!

Aqui está o JavaDoc para TypeToken .

Pense em uma abordagem mais funcional: em vez de criar algum E do nada (que é claramente um cheiro de código), passe uma function que sabe como criar um, ou seja,

 E createContents(Callable makeone) { return makeone.call(); // most simple case clearly not that useful } 

Do Java Tutorial – Restrições aos Genéricos :

Não é possível criar instâncias de parâmetros de tipo

Você não pode criar uma instância de um parâmetro de tipo. Por exemplo, o código a seguir causa um erro em tempo de compilation:

 public static  void append(List list) { E elem = new E(); // compile-time error list.add(elem); } 

Como solução alternativa, você pode criar um object de um parâmetro de tipo por meio da reflection:

 public static  void append(List list, Class cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem); } 

Você pode invocar o método append da seguinte maneira:

 List ls = new ArrayList<>(); append(ls, String.class); 

Aqui está uma opção que eu criei, pode ajudar:

 public static class Container { private Class clazz; public Container(Class clazz) { this.clazz = clazz; } public E createContents() throws Exception { return clazz.newInstance(); } } 

EDIT: Como alternativa, você pode usar esse construtor (mas requer uma instância de E):

 @SuppressWarnings("unchecked") public Container(E instance) { this.clazz = (Class) instance.getClass(); } 

Se você não quiser digitar o nome da class duas vezes durante a instanciação, como em:

 new SomeContainer(SomeType.class); 

Você pode usar o método de fábrica:

  SomeContainer createContainer(Class class); 

Como em:

 public class Container { public static  Container create(Class c) { return new Container(c); } Class c; public Container(Class c) { super(); this.c = c; } public E createInstance() throws InstantiationException, IllegalAccessException { return c.newInstance(); } } 

Quando você está trabalhando com E em tempo de compilation, você não se importa com o tipo genérico real “E” (ou você usa reflection ou trabalha com class base de tipo genérico) então deixe a subclass prover uma instância de E.

 Abstract class SomeContainer { abstract protected E createContents(); public doWork(){ E obj = createContents(); // Do the work with E } } **BlackContainer extends** SomeContainer{ Black createContents() { return new Black(); } } 

Você pode usar:

 Class.forName(String).getConstructor(arguments types).newInstance(arguments) 

Mas você precisa fornecer o nome exato da class, incluindo pacotes, por exemplo. java.io.FileInputStream . Eu usei isso para criar um analisador de expressões matemáticas.

Java infelizmente não permite o que você quer fazer. Consulte http://docs.oracle.com/javase/tutorial/java/generics/restrictions.html#createObjects

Você não pode criar uma instância de um parâmetro de tipo. Por exemplo, o código a seguir causa um erro em tempo de compilation:

 public static  void append(List list) { E elem = new E(); // compile-time error list.add(elem); } 

Como solução alternativa, você pode criar um object de um parâmetro de tipo por meio da reflection:

 public static  void append(List list, Class cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem); } 

Você pode invocar o método append da seguinte maneira:

 List ls = new ArrayList<>(); append(ls, String.class); 

Eu pensei que poderia fazer isso, mas muito desapontado: não funciona, mas acho que ainda vale a pena compartilhar.

Talvez alguém possa corrigir:

 import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; interface SomeContainer { E createContents(); } public class Main { @SuppressWarnings("unchecked") public static  SomeContainer createSomeContainer() { return (SomeContainer) Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{ SomeContainer.class }, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Class< ?> returnType = method.getReturnType(); return returnType.newInstance(); } }); } public static void main(String[] args) { SomeContainer container = createSomeContainer(); [*] System.out.println("String created: [" +container.createContents()+"]"); } } 

Produz:

 Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String at Main.main(Main.java:26) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:601) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120) 

A linha 26 é aquela com o [*] .

A única solução viável é a de @JustinRudd

Um imporovement da resposta de @ Noah.

Razão para mudança

a] É mais seguro se mais de 1 tipo genérico for usado no caso de você alterar o pedido.

b] Uma assinatura de tipo genérico de class muda de tempos em tempos para que você não se surpreenda com exceções inexplicáveis ​​no tempo de execução.

Código Robusto

 public abstract class Clazz

{ protected M model; protected void createModel() { Type[] typeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments(); for (Type type : typeArguments) { if ((type instanceof Class) && (Model.class.isAssignableFrom((Class) type))) { try { model = ((Class) type).newInstance(); } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } } } }

Ou use o forro um

Um código de linha

 model = ((Class) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance(); 

Como você disse, você não pode realmente fazer isso por causa do apagamento do tipo. Você pode fazer isso usando reflection, mas isso exige muito código e muito tratamento de erros.

Se você quer dizer new E() então é impossível. E gostaria de acrescentar que nem sempre é correto – como você sabe se E tem um construtor público no-args? Mas você sempre pode delegar a criação para alguma outra class que saiba como criar uma instância – pode ser Class ou seu código personalizado como este

 interface Factory{ E create(); } class IntegerFactory implements Factory{ private static int i = 0; Integer create() { return i++; } } 
 return (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance(); 

Você pode conseguir isso com o seguinte snippet:

 import java.lang.reflect.ParameterizedType; public class SomeContainer { E createContents() throws InstantiationException, IllegalAccessException { ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); @SuppressWarnings("unchecked") Class clazz = (Class) genericSuperclass.getActualTypeArguments()[0]; return clazz.newInstance(); } public static void main( String[] args ) throws Throwable { SomeContainer< Long > scl = new SomeContainer<>(); Long l = scl.createContents(); System.out.println( l ); } } 

Existem várias bibliotecas que podem resolver o E para você usando técnicas semelhantes ao que o artigo de Robertson discutiu. Aqui está uma implementação de createContents que usa TypeTools para resolver a class bruta representada por E:

 E createContents() throws Exception { return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance(); } 

Isso assume que getClass () resolve uma subclass de SomeContainer e falhará de outra forma, já que o valor parametrizado real de E será apagado em tempo de execução se não for capturado em uma subclass.

Aqui está uma implementação de createContents que usa TypeTools para resolver a class bruta representada por E :

 E createContents() throws Exception { return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance(); } 

Essa abordagem funciona apenas se SomeContainer for subclassificado, de forma que o valor real de E seja capturado em uma definição de tipo:

 class SomeStringContainer extends SomeContainer 

Caso contrário, o valor de E é apagado no tempo de execução e não é recuperável.

Você pode com um classloader e o nome da class, eventualmente, alguns parâmetros.

 final ClassLoader classLoader = ... final Class< ?> aClass = classLoader.loadClass("java.lang.Integer"); final Constructor< ?> constructor = aClass.getConstructor(int.class); final Object o = constructor.newInstance(123); System.out.println("o = " + o);