Como tornar estático um método Java Generic?

172

A seguir, é apresentado um trecho de como criar uma classe genérica java para anexar um único item a uma matriz. Como posso tornar appendToArray um método estático. Adicionar estático à assinatura do método resulta em erros de compilação.

public class ArrayUtils<E> {

        public E[] appendToArray(E[] array, E item) {
            E[] result = (E[])new Object[array.length+1];
            result[array.length] = item;
            return result;
        }
}
Chris Johnson
fonte
Quais erros de compilação você recebe? Além disso, por que não usar apenas um dos contêineres da biblioteca padrão?
Karl Knechtel
1
Erro de compilação: Na verdade, eu estava adicionando o modificador estático incorreto. Usando coleções: Sim, usar uma coleção seria ideal, mas a pergunta não é sobre coleções versus matriz, meu caso de uso exige uma matriz.
Chris Johnson
Observe que você precisará usar a reflexão (EVIL) para impedir que o código do cliente gere uma exceção em algumas, mas não em todas as circunstâncias (agradável). É melhor evitar matrizes de referência.
Tom Hawtin - defina

Respostas:

283

a única coisa que você pode fazer é mudar sua assinatura para

public static <E> E[] appendToArray(E[] array, E item)

Detalhes importantes:

Expressões genéricas que precedem o valor de retorno sempre introduzem (declaram) uma nova variável de tipo genérico.

Além disso, variáveis ​​de tipo entre types ( ArrayUtils) e métodos estáticos ( appendToArray) nunca interferem entre si.

Então, o que isso significa: na minha resposta <E>ocultaria o Ede ArrayUtils<E>se o método não fosse static. E <E>não tem nada a ver com o Ede ArrayUtils<E>.

Para refletir melhor esse fato, uma resposta mais correta seria:

public static <I> I[] appendToArray(I[] array, I item)
scheffield
fonte
30
Lembre-se também de que não há absolutamente nenhuma relação entre a variável de tipo no nível de classe Ee a variável de tipo de método estático E. Considero uma prática muito melhor usar um nome de variável diferente ao declarar métodos genéricos, estáticos ou não, dentro de classes genéricas.
Juiz Mental
mas, neste caso, posso passar objetos de tipos diferentes para os parâmetros. Como se eu pudesse passar o array inteiro [] como primeiro parâmetro e item duplo.
Pinkpanther
pinkpanther: Verdade, mas não faz mal, porque o método estático só opera em um objeto de matriz que é passado a ele por meio de um parâmetro, para que seus elementos tenham o tipo correto.
Dabbler
80
public static <E> E[] appendToArray(E[] array, E item) { ...

Observe o <E>.

Métodos genéricos estáticos precisam de sua própria declaração genérica ( public static <E>) separada da declaração genérica da classe ( public class ArrayUtils<E>).

Se o compilador reclamar de uma ambiguidade de tipo ao invocar um método genérico estático (novamente provavelmente não no seu caso, mas, de maneira geral, apenas no caso), veja como chamar explicitamente um método genérico estático usando um tipo específico ( _class_.<_generictypeparams_>_methodname_):

String[] newStrings = ArrayUtils.<String>appendToArray(strings, "another string");

Isso aconteceria apenas se o compilador não puder determinar o tipo genérico porque, por exemplo, o tipo genérico não está relacionado aos argumentos do método.

Bert F
fonte
10

Você precisa mover o parâmetro type para o nível do método para indicar que você tem um método genérico em vez de classe genérica:

public class ArrayUtils {
    public static <T> E[] appendToArray(E[] array, E item) {
        E[] result = (E[])new Object[array.length+1];
        result[array.length] = item;
        return result;
    }
}
axtavt
fonte
1
Isso não funcionará porque você não definiu o tipo genérico E. Nesse caso, você ainda precisa ter o tipo genérico <E> na definição de classe.
George Xavier
0

Vou explicar de uma maneira simples.

Os genéricos definidos no nível da classe são completamente separados dos genéricos definidos no nível do método (estático).

class Greet<T> {

    public static <T> void sayHello(T obj) {
        System.out.println("Hello " + obj);
    }
}

Quando você vir o código acima em qualquer lugar, observe que o T definido no nível da classe não tem nada a ver com o T definido no método estático. O código a seguir também é completamente válido e equivalente ao código acima.

class Greet<T> {

    public static <E> void sayHello(E obj) {
        System.out.println("Hello " + obj);
    }
}

Por que o método estático precisa ter seus próprios genéricos separados dos da classe?

Isso ocorre porque, o método estático pode ser chamado sem sequer instanciar a classe. Portanto, se a Classe ainda não foi instanciada, ainda não sabemos o que é T. Essa é a razão pela qual os métodos estáticos precisam ter seus próprios genéricos.

Portanto, sempre que você estiver chamando o método estático,

Greet.sayHello("Bob");
Greet.sayHello(123);

A JVM interpreta como a seguir.

Greet.<String>sayHello("Bob");
Greet.<Integer>sayHello(123);

Ambos dando as mesmas saídas.

Hello Bob
Hello 123
Vishnu Vivek
fonte