Como criar uma matriz genérica em Java?

1090

Devido à implementação de genéricos Java, você não pode ter um código como este:

public class GenSet<E> {
    private E a[];

    public GenSet() {
        a = new E[INITIAL_ARRAY_LENGTH]; // error: generic array creation
    }
}

Como posso implementar isso mantendo a segurança do tipo?

Eu vi uma solução nos fóruns Java que é assim:

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

Mas eu realmente não entendo o que está acontecendo.

tatsuhirosatou
fonte
14
Você realmente precisa usar uma matriz aqui? Que tal usar uma coleção?
mate b
12
Sim, também acho que as coleções são mais elegantes para esse problema. Mas isso é para uma aula e eles são obrigatórios :(
tatsuhirosatou 09/02/09
3
Eu não entendo por que eu preciso refletir aqui. A gramática Java é estranha: como a nova java.util.HashMap <String, String> [10] não é válida. O novo java.util.HashMap <long, long> (10) não é válido. new long [] [10] não é válido, new long [10] [] é válido. Esse material faz com que escrever um programa que possa escrever um programa em java seja mais difícil do que parece.
bronze man

Respostas:

703

Eu tenho que fazer uma pergunta em troca: você está GenSet"marcado" ou "desmarcado"? O que isso significa?

  • Verificado : digitação forte . GenSetsabe explicitamente que tipo de objetos que ele contém (ou seja, seu construtor foi explicitamente chamado com um Class<E>argumento, e métodos irá lançar uma exceção quando eles são passados argumentos que não são do tipo E. Veja Collections.checkedCollection.

    -> nesse caso, você deve escrever:

    public class GenSet<E> {
    
        private E[] a;
    
        public GenSet(Class<E> c, int s) {
            // Use Array native method to create array
            // of a type only known at run time
            @SuppressWarnings("unchecked")
            final E[] a = (E[]) Array.newInstance(c, s);
            this.a = a;
        }
    
        E get(int i) {
            return a[i];
        }
    }
  • Desmarcado : digitação fraca . Nenhuma verificação de tipo é realmente feita em nenhum dos objetos passados ​​como argumento.

    -> nesse caso, você deve escrever

    public class GenSet<E> {
    
        private Object[] a;
    
        public GenSet(int s) {
            a = new Object[s];
        }
    
        E get(int i) {
            @SuppressWarnings("unchecked")
            final E e = (E) a[i];
            return e;
        }
    }

    Observe que o tipo de componente da matriz deve ser o apagamento do parâmetro type:

    public class GenSet<E extends Foo> { // E has an upper bound of Foo
    
        private Foo[] a; // E erases to Foo, so use Foo[]
    
        public GenSet(int s) {
            a = new Foo[s];
        }
    
        ...
    }

Tudo isso resulta de uma fraqueza conhecida e deliberada de genéricos em Java: foi implementada usando apagamento, para que as classes "genéricas" não saibam com qual argumento de tipo elas foram criadas no tempo de execução e, portanto, não podem fornecer tipos. segurança, a menos que algum mecanismo explícito (verificação de tipo) seja implementado.

Varkhan
fonte
7
Qual seria a melhor opção em termos de desempenho? Eu preciso obter elementos dessa matriz com bastante frequência (dentro de um loop). Portanto, uma coleção é provavelmente mais lenta, mas qual dessas duas é a mais rápida?
user1111929
3
E se o tipo genérico estiver delimitado, a matriz de apoio deverá ser do tipo delimitador.
Mordechai #
5
@AaronDigulla Apenas para esclarecer que não é atribuição, mas a inicialização de uma variável local. Você não pode anotar uma expressão / declaração.
Kennytm
1
@Varkhan Existe uma maneira de redimensionar essas matrizes de dentro da implementação da classe. Por exemplo, se eu quiser redimensionar após o estouro, como ArrayList. Procurei a Implementação de ArrayList que eles têm Object[] EMPTY_ELEMENTDATA = {}para armazenamento. Posso usar esse mecanismo para redimensionar sem conhecer o tipo usando genéricos?
precisa saber é o seguinte
2
Para aqueles que querem fazer um método com um tipo genérico (que foi o que eu estava procurando), use o seguinte:public void <T> T[] newArray(Class<T> type, int length) { ... }
Daniel Kvist
225

Você consegue fazer isso:

E[] arr = (E[])new Object[INITIAL_ARRAY_LENGTH];

Essa é uma das maneiras sugeridas de implementar uma coleção genérica em Java eficaz; Item 26 . Sem erros de tipo, sem necessidade de converter a matriz repetidamente. No entanto, isso dispara um aviso porque é potencialmente perigoso e deve ser usado com cautela. Conforme detalhado nos comentários, isso Object[]agora está disfarçado como nosso E[]tipo e pode causar erros ou erros inesperados, ClassCastExceptionse usados ​​de maneira insegura.

Como regra geral, esse comportamento é seguro desde que a matriz de conversão seja usada internamente (por exemplo, para fazer backup de uma estrutura de dados) e não seja retornada ou exposta ao código do cliente. Se você precisar retornar uma matriz de um tipo genérico para outro código, a Arrayclasse de reflexão mencionada é o caminho certo a seguir.


Vale ressaltar que, sempre que possível, você terá mais prazer em trabalhar com Lists do que com matrizes se estiver usando genéricos. Certamente, às vezes você não tem escolha, mas o uso da estrutura de coleções é muito mais robusto.

dimo414
fonte
47
Isso não funcionará se a matriz for tratada como uma matriz digitada de qualquer tipo, como String[] s=b;no test()método acima . Isso porque a matriz de E não é realmente, é Objeto []. Isso importa se você quiser, por exemplo, um List<String>[]- você não pode usar um Object[]para isso, você deve ter um List[]especificamente. É por isso que você precisa usar a criação de matriz de classe <?> Refletida.
Lawrence Dol
8
O caso / problema de canto é se você deseja fazer, por exemplo, public E[] toArray() { return (E[])internalArray.clone(); }quando internalArrayé digitado como E[]e, portanto, é realmente um Object[]. Isso falha no tempo de execução com uma exceção de conversão de tipo porque Object[]não é possível atribuir uma matriz de qualquer tipo Eque seja.
Lawrence Dol
17
Basicamente, essa abordagem funcionará desde que você não retorne a matriz, não a passe ou armazene em algum lugar fora da classe que exija uma matriz de um determinado tipo. Contanto que você esteja dentro da classe, você está bem porque E é apagado. É "perigoso" porque se você tentar devolvê-lo ou algo assim, não receberá nenhum aviso de que é inseguro. Mas se você for cuidadoso, funciona.
Newacct
3
É bastante seguro. Em E[] b = (E[])new Object[1];você pode ver claramente que a única referência para a matriz criada éb e que o tipo de bé E[]. Portanto, não há perigo de você acessar acidentalmente a mesma matriz por meio de uma variável diferente de um tipo diferente. Se sim, você Object[] a = new Object[1]; E[]b = (E[])a; precisaria ser paranóico sobre como usar a.
Aaron McDaid
5
Pelo menos no Java 1.6, isso gera um aviso: "Elenco não verificado de Object [] para T []"
Quantum7 24/03/12
61

Veja como usar os genéricos para obter uma matriz precisamente do tipo que você procura, preservando a segurança do tipo (ao contrário das outras respostas, que lhe devolvem uma Objectmatriz ou resultam em avisos em tempo de compilação):

import java.lang.reflect.Array;  

public class GenSet<E> {  
    private E[] a;  

    public GenSet(Class<E[]> clazz, int length) {  
        a = clazz.cast(Array.newInstance(clazz.getComponentType(), length));  
    }  

    public static void main(String[] args) {  
        GenSet<String> foo = new GenSet<String>(String[].class, 1);  
        String[] bar = foo.a;  
        foo.a[0] = "xyzzy";  
        String baz = foo.a[0];  
    }  
}

Isso é compilado sem avisos e, como você pode ver main, para qualquer tipo de declaração de instância GenSet, você pode atribuir aa uma matriz desse tipo e atribuir um elemento dea a uma variável desse tipo, o que significa que a matriz e os valores na matriz são do tipo correto.

Ele funciona usando literais de classe como tokens de tipo de tempo de execução, conforme discutido no Tutoriais Java . Literais de classe são tratados pelo compilador como instâncias de java.lang.Class. Para usar um, basta seguir o nome de uma classe com .class. Então, String.classatua como um Classobjeto representando a classe String. Isso também funciona para interfaces, enumerações, matrizes unidimensionais (por exemplo String[].class), primitivas (por exemplo int.class) e a palavra-chave void( por exemplo ,void.class ).

Classem si é genérico (declarado como Class<T>, onde Trepresenta o tipo que o Classobjeto está representando), o que significa que o tipo de String.classéClass<String> .

Portanto, sempre que você chama o construtor GenSet, transmite uma classe literal para o primeiro argumento que representa uma matriz do GenSettipo declarado da instância (por exemplo, String[].classparaGenSet<String> ). Observe que você não poderá obter uma matriz de primitivas, pois as primitivas não podem ser usadas para variáveis ​​de tipo.

Dentro do construtor, chamar o método castretorna o Objectargumento transmitido para a classe representada pelo Classobjeto no qual o método foi chamado. Chamar o método estático newInstanceem java.lang.reflect.Arrayretorna como uma Objectmatriz do tipo representado pelo Classobjeto passado como o primeiro argumento e do comprimento especificado pelo métodoint passado como o segundo argumento. Chamar o método getComponentTypedevolve um Classobjecto que representa o tipo de componente da matriz representada pelo Classobjecto em que o método foi chamado (por exemplo, String.classpara String[].class, nullse oClass objecto não representa uma matriz).

Essa última frase não é totalmente precisa. ChamandoString[].class.getComponentType() retorna um Classobjeto que representa a classe String, mas seu tipo Class<?>não é , Class<String>e é por isso que você não pode fazer algo como o seguinte.

String foo = String[].class.getComponentType().cast("bar"); // won't compile

O mesmo vale para todos os métodos Classque retornam umClass objeto.

Sobre o comentário de Joachim Sauer em esta resposta (eu não tenho reputação suficiente para comentar sobre ela), o exemplo do uso do elenco T[]resultará em um aviso, pois o compilador não pode garantir a segurança do tipo nesse caso.


Edite sobre os comentários de Ingo:

public static <T> T[] newArray(Class<T[]> type, int size) {
   return type.cast(Array.newInstance(type.getComponentType(), size));
}
gdejohn
fonte
5
Isso é inútil, é apenas uma maneira complicada de escrever nova String [...]. Mas o que é realmente necessário é algo como estática pública <T> T [] newArray (int size) {...}, e isso simplesmente não existe no java noir, pode ser simulado com reflexão - a razão é que informações sobre como um tipo genérico instanciado não está disponível em tempo de execução.
Ingo
4
@Ingo Do que você está falando? Meu código pode ser usado para criar uma matriz de qualquer tipo.
Gdejohn
3
@Charlatan: Claro, mas também o novo []. A questão é: quem sabe o tipo e quando. Portanto, se tudo que você tem é um tipo genérico, não pode.
Ingo
2
Eu não duvido disso. O ponto é, você não receber um objeto de classe em tempo de execução para o tipo genérico X.
Ingo
2
Quase. Admito que isso é mais do que aquilo que pode ser alcançado com o novo []. Na prática, isso quase sempre faz o trabalho. No entanto, ainda não é possível, por exemplo, escrever uma classe de contêiner parametrizada com E que possua o método E [] toArray () e que realmente retorne uma matriz E [] verdadeira. Seu código pode ser aplicado apenas quando houver pelo menos um objeto E na coleção. Portanto, uma solução geral é impossível.
Ingo
42

Esta é a única resposta que é do tipo seguro

E[] a;

a = newArray(size);

@SafeVarargs
static <E> E[] newArray(int length, E... array)
{
    return Arrays.copyOf(array, length);
}
irreputável
fonte
Eu tive que procurar, mas sim, o segundo argumento "length" para Arrays#copyOf()é independente do comprimento da matriz fornecida como o primeiro argumento. Isso é inteligente, apesar de pagar o custo das ligações para Math#min()e System#arrayCopy(), nenhuma das quais é estritamente necessária para realizar esse trabalho. docs.oracle.com/javase/7/docs/api/java/util/…
seh
8
Isso não funciona se Efor uma variável de tipo. O varargs cria uma matriz de apagamento de Equando Eé uma variável de tipo, tornando-a não muito diferente de (E[])new Object[n]. Por favor, consulte http://ideone.com/T8xF91 . Não é, de maneira alguma, mais seguro do que qualquer outra resposta.
Radiodef 6/04
1
@ Radiodef - a solução é comprovadamente segura em termos de tipo em tempo de compilação. observe que o apagamento não faz exatamente parte das especificações de idioma; as especificações são escritas cuidadosamente para que possamos ter uma reificação completa no futuro - e essa solução também funcionaria perfeitamente em tempo de execução, ao contrário de outras soluções.
ZhongYu
@ Radiodef - É discutível se banir a criação genérica de array é uma boa idéia. independentemente, o idioma deixa um backdoor - o vararg requer criação genérica de array. É tão bom como se o idioma tivesse permitido new E[]. O problema que você mostrou no seu exemplo é um problema geral de apagamento, não exclusivo desta pergunta e desta resposta.
ZhongYu
2
@ Radiodef - Existem algumas diferenças. A correção dessa solução é verificada pelo compilador; não depende do raciocínio humano do elenco forçado. A diferença não é significativa para esse problema específico. Algumas pessoas gostam de ser um pouco chiques, só isso. Se alguém é enganado pelas palavras do OP, isso é esclarecido pelos seus comentários e pelos meus.
ZhongYu
33

Para estender a mais dimensões, basta adicionar []'s e parâmetros de dimensão a newInstance()( Té um parâmetro de tipo, clsé a Class<T>, d1através de d5números inteiros):

T[] array = (T[])Array.newInstance(cls, d1);
T[][] array = (T[][])Array.newInstance(cls, d1, d2);
T[][][] array = (T[][][])Array.newInstance(cls, d1, d2, d3);
T[][][][] array = (T[][][][])Array.newInstance(cls, d1, d2, d3, d4);
T[][][][][] array = (T[][][][][])Array.newInstance(cls, d1, d2, d3, d4, d5);

Veja Array.newInstance()para detalhes.

Jason C
fonte
4
+1 Houve perguntas sobre a criação de matrizes multidimensionais que foram encerradas como bobagens desta postagem - mas nenhuma resposta abordou isso especificamente.
Paul Bellora
1
@JordanC Maybe; embora seja o mesmo em espírito que stackoverflow.com/a/5671304/616460 ; Vou pensar na melhor maneira de lidar amanhã. Estou com sono.
Jason C
14

No Java 8, podemos criar um tipo de criação genérica de array usando uma referência lambda ou método. Isso é semelhante à abordagem reflexiva (que passa por a Class), mas aqui não estamos usando reflexão.

@FunctionalInterface
interface ArraySupplier<E> {
    E[] get(int length);
}

class GenericSet<E> {
    private final ArraySupplier<E> supplier;
    private E[] array;

    GenericSet(ArraySupplier<E> supplier) {
        this.supplier = supplier;
        this.array    = supplier.get(10);
    }

    public static void main(String[] args) {
        GenericSet<String> ofString =
            new GenericSet<>(String[]::new);
        GenericSet<Double> ofDouble =
            new GenericSet<>(Double[]::new);
    }
}

Por exemplo, isso é usado por <A> A[] Stream.toArray(IntFunction<A[]>).

Isso também pode ser feito antes do Java 8 usando classes anônimas, mas é mais complicado.

Radiodef
fonte
Você realmente não precisa de uma interface especial como ArraySupplieressa, pode declarar o construtor como GenSet(Supplier<E[]> supplier) { ...e chamá-lo com a mesma linha que possui.
Lii
4
@ Li Para ser o mesmo que o meu exemplo, seria IntFunction<E[]>, mas sim, isso é verdade.
Radiodef 28/12
11

Isso é abordado no Capítulo 5 (Genéricos) do Java efetivo, 2ª edição , item 25 ... Prefere listas a matrizes

Seu código funcionará, embora gere um aviso desmarcado (que você pode suprimir com a seguinte anotação:

@SuppressWarnings({"unchecked"})

No entanto, provavelmente seria melhor usar uma lista em vez de uma matriz.

Há uma discussão interessante sobre esse bug / recurso no site do projeto OpenJDK .

Jeff Olson
fonte
8

Você não precisa passar o argumento Class para o construtor. Tente isso.

public class GenSet<T> {
    private final T[] array;
    @SuppressWarnings("unchecked")
    public GenSet(int capacity, T... dummy) {
        if (dummy.length > 0)
            throw new IllegalArgumentException(
              "Do not provide values for dummy argument.");
        Class<?> c = dummy.getClass().getComponentType();
        array = (T[])Array.newInstance(c, capacity);
    }
    @Override
    public String toString() {
        return "GenSet of " + array.getClass().getComponentType().getName()
            + "[" + array.length + "]";
    }
}

e

GenSet<Integer> intSet = new GenSet<>(3);
System.out.println(intSet);
System.out.println(new GenSet<String>(2));

resultado:

GenSet of java.lang.Integer[3]
GenSet of java.lang.String[2]
saka1029
fonte
7

Os genéricos Java funcionam verificando tipos em tempo de compilação e inserindo conversões apropriadas, mas apagando os tipos nos arquivos compilados. Isso torna as bibliotecas genéricas utilizáveis ​​por código que não entende os genéricos (que foi uma decisão deliberada de design), mas que significa que você normalmente não pode descobrir qual é o tipo em tempo de execução.

O Stack(Class<T> clazz,int capacity)construtor público exige que você passe um objeto Class em tempo de execução, o que significa que as informações da classe estão disponíveis no tempo de execução para o código que precisa delas. E aClass<T> forma significa que o compilador verificará se o objeto Class que você passa é precisamente o objeto Class para o tipo T. Não é uma subclasse de T, nem uma superclasse de T, mas precisamente T.

Isso significa que você pode criar um objeto de matriz do tipo apropriado em seu construtor, o que significa que o tipo de objetos que você armazena em sua coleção terá seus tipos verificados no ponto em que foram adicionados à coleção.

Bill Michell
fonte
6

Olá, embora o tópico esteja inoperante, gostaria de chamar sua atenção para isso:

Os genéricos são usados ​​para verificação de tipo durante o tempo de compilação:

  • Portanto, o objetivo é verificar se o que entra é o que você precisa.
  • O que você retorna é o que o consumidor precisa.
  • Verifique isto:

insira a descrição da imagem aqui

Não se preocupe com avisos de conversão de tipos ao escrever classe genérica. Se preocupe quando você estiver usando.

puneeth
fonte
6

E esta solução?

@SafeVarargs
public static <T> T[] toGenericArray(T ... elems) {
    return elems;
}

Funciona e parece simples demais para ser verdade. Existe alguma desvantagem?

Benjamin M
fonte
3
Limpo, mas só funciona se você o chamar 'manualmente', ou seja, passar os elementos individualmente. Se você não pode criar uma nova instância de T[], então você não pode criar programaticamente a T[] elemspara passar para a função. E se você pudesse, não precisaria da função.
Orlade 29/08/16
5

Veja também este código:

public static <T> T[] toArray(final List<T> obj) {
    if (obj == null || obj.isEmpty()) {
        return null;
    }
    final T t = obj.get(0);
    final T[] res = (T[]) Array.newInstance(t.getClass(), obj.size());
    for (int i = 0; i < obj.size(); i++) {
        res[i] = obj.get(i);
    }
    return res;
}

Ele converte uma lista de qualquer tipo de objeto em uma matriz do mesmo tipo.

MatheusJardimB
fonte
Sim, você retorna nulo, que não é o array vazio esperado. É o melhor que você pode fazer, mas não o ideal.
Kevin Cox
Isso também pode falhar se Listtiver mais de um tipo de objeto, por exemplo, toArray(Arrays.asList("abc", new Object()))será lançado ArrayStoreException.
Radiodef 06/04
Eu usei uma versão simplificada disso; primeira coisa que pude usar que funcionou, apesar de admitir que não tentei algumas das soluções mais envolvidas. Para evitar um forloop e outros que usei, Arrays.fill(res, obj);pois queria o mesmo valor para cada índice.
bbarker
5

Eu encontrei uma maneira rápida e fácil que funciona para mim. Observe que eu usei isso apenas no Java JDK 8. Não sei se ele funcionará com versões anteriores.

Embora não possamos instanciar uma matriz genérica de um parâmetro de tipo específico, podemos passar uma matriz já criada para um construtor de classe genérico.

class GenArray <T> {
    private T theArray[]; // reference array

    // ...

    GenArray(T[] arr) {
        theArray = arr;
    }

    // Do whatever with the array...
}

Agora, no principal, podemos criar a matriz da seguinte maneira:

class GenArrayDemo {
    public static void main(String[] args) {
        int size = 10; // array size
        // Here we can instantiate the array of the type we want, say Character (no primitive types allowed in generics)
        Character[] ar = new Character[size];

        GenArray<Character> = new Character<>(ar); // create the generic Array

        // ...

    }
}

Para mais flexibilidade com suas matrizes, você pode usar uma lista vinculada, por exemplo. o ArrayList e outros métodos encontrados na classe Java.util.ArrayList.

Nikos
fonte
4

O exemplo está usando a reflexão Java para criar uma matriz. Isso geralmente não é recomendado, pois não é seguro. Em vez disso, o que você deve fazer é usar uma lista interna e evitar a matriz.

Ola Bini
fonte
13
O segundo exemplo (usando Array.newInstance ()) é de fato seguro. Isso é possível porque o tipo T do objeto Class precisa corresponder ao T da matriz. Basicamente, obriga a fornecer as informações que o Java Runtime descarta para genéricos.
Joachim Sauer
4

Passando uma lista de valores ...

public <T> T[] array(T... values) {
    return values;
}
Rodrigo Asensio
fonte
3

Criei esse trecho de código para instanciar reflexivamente uma classe que é passada para um simples utilitário de teste automatizado.

Object attributeValue = null;
try {
    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }
    else if(!clazz.isInterface()){
        attributeValue = BeanUtils.instantiateClass(clazz);
    }
} catch (Exception e) {
    logger.debug("Cannot instanciate \"{}\"", new Object[]{clazz});
}

Observe este segmento:

    if(clazz.isArray()){
        Class<?> arrayType = clazz.getComponentType();
        attributeValue = Array.newInstance(arrayType, 0);
    }

para o início da matriz em que Array.newInstance (classe da matriz, tamanho da matriz) . A classe pode ser primitiva (int.class) e objeto (Integer.class).

BeanUtils faz parte da primavera.

Bobster
fonte
3

Na verdade, uma maneira mais fácil de fazer isso é criar uma matriz de objetos e convertê-la no tipo desejado, como no exemplo a seguir:

T[] array = (T[])new Object[SIZE];

onde SIZEé uma constante e Té um identificador de tipo

Pedram Esmaeeli
fonte
1

O elenco forçado sugerido por outras pessoas não funcionou para mim, lançando uma exceção do elenco ilegal.

No entanto, esse elenco implícito funcionou bem:

Item<K>[] array = new Item[SIZE];

onde Item é uma classe que eu defini contendo o membro:

private K value;

Dessa forma, você obtém uma matriz do tipo K (se o item tiver apenas o valor) ou qualquer tipo genérico que você deseja definir na classe Item.

vnportnoy
fonte
1

Ninguém mais respondeu à pergunta do que está acontecendo no exemplo que você postou.

import java.lang.reflect.Array;

class Stack<T> {
    public Stack(Class<T> clazz, int capacity) {
        array = (T[])Array.newInstance(clazz, capacity);
    }

    private final T[] array;
}

Como outros já disseram, os genéricos são "apagados" durante a compilação. Portanto, em tempo de execução, uma instância de um genérico não sabe qual é o seu tipo de componente. A razão para isso é histórica, a Sun queria adicionar genéricos sem interromper a interface existente (de origem e binária).

As matrizes, por outro lado , conhecem seu tipo de componente em tempo de execução.

Este exemplo soluciona o problema fazendo com que o código que chama o construtor (que conhece o tipo) passe um parâmetro informando à classe o tipo necessário.

Portanto, o aplicativo construiria a classe com algo como

Stack<foo> = new Stack<foo>(foo.class,50)

e o construtor agora sabe (em tempo de execução) qual é o tipo de componente e pode usar essas informações para construir a matriz por meio da API de reflexão.

Array.newInstance(clazz, capacity);

Finalmente, temos uma conversão de tipo porque o compilador não tem como saber que a matriz retornada por Array#newInstance()é do tipo correto (mesmo que saibamos).

Esse estilo é um pouco feio, mas às vezes pode ser a solução menos ruim para a criação de tipos genéricos que precisam conhecer o tipo de componente em tempo de execução por qualquer motivo (criação de matrizes ou instâncias do tipo de componente etc.).

plugwash
fonte
1

Eu encontrei uma espécie de solução para esse problema.

A linha abaixo gera erro de criação de matriz genérica

List<Person>[] personLists=new ArrayList<Person>()[10];

No entanto, se eu encapsular List<Person>em uma classe separada, ele funcionará.

import java.util.ArrayList;
import java.util.List;


public class PersonList {

    List<Person> people;

    public PersonList()
    {
        people=new ArrayList<Person>();
    }
}

Você pode expor pessoas da classe PersonList através de um getter. A linha abaixo fornecerá uma matriz que possui um List<Person>em cada elemento. Em outras palavras, matriz de List<Person>.

PersonList[] personLists=new PersonList[10];

Eu precisava de algo assim em algum código no qual estava trabalhando e foi isso que fiz para fazê-lo funcionar. Até agora sem problemas.

developer747
fonte
0

Você pode criar uma matriz de objetos e convertê-la em E em qualquer lugar. Sim, não é uma maneira muito limpa de fazê-lo, mas deve pelo menos funcionar.

Esko
fonte
"Estamos procurando respostas longas que forneçam alguma explicação e contexto. Não dê apenas uma resposta em uma linha; explique por que sua resposta está correta, de preferência com citações. Respostas sem explicações podem ser removidas."
gparyani
Mas, em alguns casos, isso não funcionará, como se sua classe genérica quisesse implementar uma interface comparável.
RamPrasadBismil
Bem-vindo a sete anos atrás, suponho.
Esko
1
Isso não funcionará se você tentar retornar a matriz do código genérico para um chamador não genérico. Haverá uma exceção de classe classificatória.
plugwash
0

tente isso.

private int m = 0;
private int n = 0;
private Element<T>[][] elements = null;

public MatrixData(int m, int n)
{
    this.m = m;
    this.n = n;

    this.elements = new Element[m][n];
    for (int i = 0; i < m; i++)
    {
        for (int j = 0; j < n; j++)
        {
            this.elements[i][j] = new Element<T>();
        }
    }
}
David Bernard
fonte
Não consigo executar seu código. De onde vem sua Elementclasse?
0

Uma solução fácil, embora desarrumada, seria aninhar uma segunda classe "portadora" dentro da sua classe principal e usá-la para armazenar seus dados.

public class Whatever<Thing>{
    private class Holder<OtherThing>{
        OtherThing thing;
    }
    public Holder<Thing>[] arrayOfHolders = new Holder<Thing>[10]
}
StarMonkey
fonte
3
Isso realmente não funciona. new Holder<Thing>[10]é uma criação genérica de array.
Radiodef 10/03
0

Talvez não esteja relacionado a esta pergunta, mas enquanto eu estava recebendo o generic array creationerro " " ao usar

Tuple<Long,String>[] tupleArray = new Tuple<Long,String>[10];

Descobri os seguintes trabalhos (e trabalhei para mim) com @SuppressWarnings({"unchecked"}):

 Tuple<Long, String>[] tupleArray = new Tuple[10];
Mohsen Afshin
fonte
Sim, isso não está muito relacionado, mas está enraizado nos mesmos problemas (apagamento, covariância de array). Aqui está um exemplo de um post sobre a criação de matrizes de tipos parametrizados: stackoverflow.com/questions/9542076/...
Paul Bellora
0

Gostaria de saber se esse código criaria uma matriz genérica eficaz?

public T [] createArray(int desiredSize){
    ArrayList<T> builder = new ArrayList<T>();
    for(int x=0;x<desiredSize;x++){
        builder.add(null);
    }
    return builder.toArray(zeroArray());
}

//zeroArray should, in theory, create a zero-sized array of T
//when it is not given any parameters.

private T [] zeroArray(T... i){
    return i;
}

Edit: Talvez uma maneira alternativa de criar uma matriz desse tipo, se o tamanho exigido fosse conhecido e pequeno, fosse simplesmente alimentar o número necessário de "null" s no comando zeroArray?

Embora obviamente isso não seja tão versátil quanto usar o código createArray.

Cambot
fonte
Não, isso não funciona. O varargs cria apagamento de Tquando Té uma variável de tipo, ou seja, zeroArrayretorna an Object[]. Consulte http://ideone.com/T8xF91 .
Radiodef 06/04
0

Você pode usar um elenco:

public class GenSet<Item> {
    private Item[] a;

    public GenSet(int s) {
        a = (Item[]) new Object[s];
    }
}
samir benzenine
fonte
Se você vai sugerir isso, realmente precisa explicar suas limitações. Nunca exponha aa fora da classe!
Radiodef
0

Na verdade, encontrei uma solução bastante exclusiva para ignorar a incapacidade de iniciar uma matriz genérica. O que você precisa fazer é criar uma classe que aceite a variável genérica T da seguinte forma:

class GenericInvoker <T> {
    T variable;
    public GenericInvoker(T variable){
        this.variable = variable;
    }
}

e, em seguida, na sua classe array, comece com o seguinte:

GenericInvoker<T>[] array;
public MyArray(){
    array = new GenericInvoker[];
}

iniciar um new Generic Invoker[]causará um problema com desmarcado, mas não deve haver nenhum problema.

Para obter da matriz, você deve chamar a matriz [i] .variable da seguinte forma:

public T get(int index){
    return array[index].variable;
}

O restante, como redimensionar a matriz, pode ser feito com Arrays.copyOf () da seguinte forma:

public void resize(int newSize){
    array = Arrays.copyOf(array, newSize);
}

E a função add pode ser adicionada da seguinte forma:

public boolean add(T element){
    // the variable size below is equal to how many times the add function has been called 
    // and is used to keep track of where to put the next variable in the array
    arrays[size] = new GenericInvoker(element);
    size++;
}
Nebulosa do Caranguejo
fonte
1
A questão era criar uma matriz do tipo do parâmetro de tipo genérico T, não uma matriz de algum tipo parametrizado.
Sotirios Delimanolis
No entanto, ele conclui a mesma tarefa e não exige que você forme uma classe, facilitando o uso de sua coleção personalizada.
Nebulosa do Caranguejo
Que tarefa ? É literalmente uma tarefa diferente: uma matriz de um tipo paramaterizado versus uma matriz de um parâmetro de tipo genérico.
Sotirios Delimanolis 30/06
Ele permite que você crie uma matriz a partir de um tipo genérico? O problema original era inicializar uma matriz usando um tipo genérico que, usando o meu método, você pode fazer isso sem precisar que o usuário envie uma classe ou cometa um erro não verificado, como tentar converter um Objeto em uma String. Como frio, eu não sou o melhor no que faço, e não fui à escola para programar, mas acho que ainda mereço um pouco de contribuição, em vez de ser denunciado por outra criança na internet.
Nebulosa do Caranguejo
Eu concordo com Sotiros. Existem duas maneiras de pensar na resposta. Ou é uma resposta para uma pergunta diferente ou é uma tentativa de generalizar a pergunta. Ambos estão errados / não ajudam. As pessoas que estão procurando orientação sobre como implementar uma classe "array genérico" devem parar de ler quando lêem o título da pergunta. E quando eles encontram um Q com 30 respostas, é altamente improvável que rolem até o final e leiam uma resposta de voto zero de um novato em SO.
Stephen C
0

De acordo com vnportnoy, a sintaxe

GenSet<Integer> intSet[] = new GenSet[3];

cria uma matriz de referências nulas, a serem preenchidas como

for (int i = 0; i < 3; i++)
{
   intSet[i] = new GenSet<Integer>();
}

qual é o tipo seguro.

Sam Ginrich
fonte
-1
private E a[];
private int size;

public GenSet(int elem)
{
    size = elem;
    a = (E[]) new E[size];
}
Zubair Ibrhaim
fonte
Você sempre deve adicionar uma explicação ao seu código e explicar por que ele resolve a pergunta postada original.
Mjarez #
-1

A criação genérica de array não é permitida em java, mas você pode fazê-lo como

class Stack<T> {
private final T[] array;
public Stack(int capacity) {
    array = (T[]) new Object[capacity];
 }
}
Irfan Ul Haq
fonte