É possível criar uma instância de um tipo genérico em Java? Estou pensando com base no que vi que a resposta é no
( devido ao apagamento do tipo ), mas eu estaria interessado se alguém puder ver algo que estou perdendo:
class SomeContainer<E>
{
E createContents()
{
return what???
}
}
EDIT: Acontece que os Super Type Tokens podem ser usados para resolver meu problema, mas requer muito código baseado em reflexão, como indicaram algumas das respostas abaixo.
Vou deixar isso em aberto por um tempo para ver se alguém apresenta algo dramaticamente diferente do Artigo Artima de Ian Robertson .
Respostas:
Você está certo. Você não pode fazer
new E()
. Mas você pode mudar paraÉ uma dor. Mas funciona. Envolver isso no padrão de fábrica o torna um pouco mais tolerável.
fonte
Class<?>
referência usando o Guava e o TypeToken; veja esta resposta para obter o código e os links!Não sei se isso ajuda, mas quando você subclasse (incluindo anonimamente) um tipo genérico, as informações do tipo estão disponíveis via reflexão. por exemplo,
Então, quando você subclassifica Foo, obtém uma instância de Bar, por exemplo,
Mas dá muito trabalho e funciona apenas para subclasses. Pode ser útil embora.
fonte
Foo
não for abstrata. Mas por que funciona apenas em subclasses anônimas de Foo? Suponha que façamosFoo
concreto (deixamos de foraabstract
), por quenew Foo<Bar>();
resultará em erro, enquantonew Foo<Bar>(){};
não? (Exceção: "A classe não pode ser convertida em ParameterizedType")<E>
emclass Foo<E>
não está limitado a qualquer tipo particular. Você vai ver o comportamento excepcional, sempre queE
não é estaticamente ligado, como em:new Foo<Bar>()
,new Foo<T>() {...}
ouclass Fizz <E> extends Foo<E>
. O primeiro caso não é estaticamente vinculado, é apagado no momento da compilação. O segundo caso substitui outra variável de tipo (T) no lugar deE
mas ainda não está vinculada. E, no último caso, deve ser óbvio queE
ainda não está vinculado.class Fizz extends Foo<Bar>
- nesse caso, os usuários deFizz
obtêm algo que é aFoo<Bar>
e não podem ser nada além de aFoo<Bar>
. Portanto, nesse caso, o compilador pode codificar essas informações nos metadados da classeFizz
e disponibilizá-las como umParameterizedType
código de reflexão. Quando você cria uma classe interna anônima comonew Foo<Bar>() {...}
se estivesse fazendo a mesma coisa, exceto queFizz
o compilador gera um nome de classe "anônimo" que você não saberá até que a classe externa seja compilada.Foo<Bar<Baz>>
,. Você criará uma instância daParameterizedTypeImpl
qual não pode ser criada explicitamente. Portanto, é uma boa ideia verificar segetActualTypeArguments()[0]
está retornando aParameterizedType
. Se for, você deseja obter o tipo bruto e criar uma instância disso.No Java 8, você pode usar a
Supplier
interface funcional para conseguir isso com bastante facilidade:Você construiria essa classe assim:
A sintaxe
String::new
nessa linha é uma referência de construtor .Se o seu construtor usa argumentos, você pode usar uma expressão lambda:
fonte
SomeContainer stringContainer = new SomeContainer(String::new);
?Você precisará de algum tipo de fábrica abstrata de um tipo ou de outro para passar o dinheiro para:
fonte
Factory<>
é uma interface e, portanto, não há corpo. O ponto é que você precisa de uma camada de indiretos para passar o buck para métodos que "conhecem" o código necessário para construir uma instância. É muito melhor fazer isso com código normal, em vez de metalinguísticoClass
ouConstructor
porque a reflexão traz todo um mundo de mágoa.SomeContainer<SomeElement> cont = new SomeContainer<>(SomeElement::new);
fonte
class GenericHome<T> extends Home<T>{}
Se você precisar de uma nova instância de um argumento de tipo dentro de uma classe genérica, faça com que seus construtores exijam sua classe ...
Uso:
Prós:
Contras:
Foo<L>
prova. Para iniciantes ...newInstance()
lançará um wobbler se a classe de argumentos do tipo não tiver um construtor padrão. Isso se aplica a todas as soluções conhecidas, de qualquer maneira.fonte
Você pode fazer isso agora e não requer um monte de código de reflexão.
Obviamente, se você precisar chamar o construtor que exigirá alguma reflexão, mas isso está muito bem documentado, esse truque não é!
Aqui está o JavaDoc para TypeToken .
fonte
Pense em uma abordagem mais funcional: em vez de criar um E do nada (que é claramente um cheiro de código), passe uma função que saiba como criar um, ou seja,
fonte
Exception
usoSupplier<E>
.Do tutorial em Java - restrições sobre genéricos :
Não é possível criar instâncias dos 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 compilação:
Como solução alternativa, você pode criar um objeto de um parâmetro de tipo por meio de reflexão:
Você pode invocar o método append da seguinte maneira:
fonte
Aqui está uma opção que eu criei, que pode ajudar:
EDIT: Como alternativa, você pode usar este construtor (mas requer uma instância de E):
fonte
Se você não quiser digitar o nome da classe duas vezes durante a instanciação, como em:
Você pode usar o método de fábrica:
Como em:
fonte
Infelizmente, o Java não permite o que você deseja fazer. Veja a solução oficial :
fonte
Quando você trabalha com E em tempo de compilação, você realmente não se importa com o tipo genérico real "E" (você usa reflexão ou trabalha com a classe base do tipo genérico); portanto, deixe a subclasse fornecer a instância de E.
fonte
Você pode usar:
Mas você precisa fornecer o nome exato da classe, incluindo pacotes, por exemplo.
java.io.FileInputStream
. Eu usei isso para criar um analisador de expressões matemáticas.fonte
foo.getClass().getName()
. De onde vem essa instância? Atualmente, estou passando um para um construtor no projeto em que estou trabalhando agora.Espero que não seja tarde demais para ajudar !!!
Java é segurança de tipo, apenas o Object pode criar instância.
No meu caso, não posso passar parâmetros para o
createContents
método Minha solução é usar extensões, em vez de todas as respostas abaixo.Este é o meu caso de exemplo que não consigo passar parâmetros.
Usar reflexão criar erro de tempo de execução, se você estender sua classe genérica com nenhum tipo de objeto. Para estender seu tipo genérico para objeto, converta esse erro em erro de tempo de compilação.
fonte
Use a
TypeToken<T>
classe:fonte
(T) new TypeToken<T>(getClass()){}.getRawType().newInstance();
Eu pensei que poderia fazer isso, mas muito decepcionado: não funciona, mas acho que ainda vale a pena compartilhar.
Talvez alguém possa corrigir:
Produz:
A linha 26 é a que possui o
[*]
.A única solução viável é a de @JustinRudd
fonte
Uma melhoria da resposta de @ Noah.
Razão para mudança
a] É mais seguro se mais de um tipo genérico for usado, caso você tenha alterado a ordem.
b] Uma assinatura de tipo genérico de classe 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
Ou use o forro
Código de uma linha
fonte
o que você pode fazer é -
Primeiro declare a variável dessa classe genérica
2.Em seguida, faça um construtor e instanciar esse objeto
Em seguida, use-o onde quiser
exemplo-
1
2
3)
fonte
Como você disse, você realmente não pode fazer isso devido ao apagamento do tipo. Você pode fazer isso usando reflexão, mas requer muito código e muito tratamento de erros.
fonte
Se você quer dizer
new E()
, é impossível. E eu acrescentaria que nem sempre é correto - como você sabe se E tem um construtor público sem argumentos? Mas você sempre pode delegar a criação para alguma outra classe que sabe como criar uma instância - pode serClass<E>
ou seu código personalizado como estefonte
fonte
SomeContainer
é simplesObject
. Portanto,this.getClass().getGenericSuperclass()
retorna aClass
(classe java.lang.Object), não aParameterizedType
. Na verdade, isso já foi indicado pela resposta dos colegas stackoverflow.com/questions/75175/… .Você pode conseguir isso com o seguinte snippet:
fonte
Existem várias bibliotecas que podem ser resolvidas
E
por você usando técnicas semelhantes às discutidas no artigo de Robertson. Aqui está uma implementaçãocreateContents
que usa TypeTools para resolver a classe bruta representada por E:Isso pressupõe que getClass () resolva para uma subclasse de SomeContainer e falhará caso contrário, já que o valor parametrizado real de E será apagado no tempo de execução, se não for capturado em uma subclasse.
fonte
Aqui está uma implementação
createContents
que usa TypeTools para resolver a classe bruta representada porE
:Essa abordagem funciona apenas se
SomeContainer
for subclassificada, portanto o valor real deE
é capturado em uma definição de tipo:Caso contrário, o valor de E é apagado no tempo de execução e não é recuperável.
fonte
Você pode com um carregador de classe e o nome da classe, eventualmente, alguns parâmetros.
fonte
Aqui está uma solução aprimorada, baseada em
ParameterizedType.getActualTypeArguments
, já mencionado por @noah, @Lars Bohl e alguns outros.Primeira pequena melhoria na implementação. Factory não deve retornar instância, mas um tipo. Assim que você retorna a instância usando
Class.newInstance()
você reduz um escopo de uso. Porque apenas os construtores sem argumentos podem ser chamados assim. Uma maneira melhor é retornar um tipo e permitir que um cliente escolha qual construtor ele deseja chamar:Aqui estão alguns exemplos de uso. @Lars Bohl mostrou apenas uma maneira significativa de obter genéricos reificados via extensão. @noah apenas através da criação de uma instância com
{}
. Aqui estão os testes para demonstrar os dois casos:Nota: você pode forçar os clientes de
TypeReference
sempre uso{}
quando instância é criada, fazendo esta classe abstrata:public abstract class TypeReference<T>
. Eu não fiz isso, apenas para mostrar o caso de teste apagado.fonte