¿Cómo hacer que un método genérico de Java sea estático?

173

El siguiente es un fragmento sobre cómo hacer que una clase genérica de Java agregue un solo elemento a una matriz. ¿Cómo puedo hacer appendToArray un método estático? Agregar estática a la firma del método da como resultado errores de compilación.

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
fuente
¿Qué errores de compilación obtienes? Además, ¿por qué no usar solo uno de los contenedores de biblioteca estándar?
Karl Knechtel
1
Error de compilación: en realidad estaba agregando el modificador estático incorrecto. Usar colecciones: Sí, usar una colección sería ideal, pero la pregunta no es sobre colecciones frente a matrices, mi caso de uso requiere una matriz.
Chris Johnson
Tenga en cuenta que deberá usar la reflexión (MAL) para evitar que el código del cliente arroje una excepción en algunas circunstancias, pero no en todas (agradable). Es mejor evitar las matrices de referencia.
Tom Hawtin - tackline

Respuestas:

283

lo único que puede hacer es cambiar su firma a

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

Detalles importantes:

Las expresiones genéricas que preceden al valor de retorno siempre introducen (declaran) una nueva variable de tipo genérico.

Además, las variables de tipo entre los tipos ( ArrayUtils) y los métodos estáticos ( appendToArray) nunca interfieren entre sí.

Por lo tanto, ¿qué significa esto: En mi respuesta <E>sería ocultar la Ede ArrayUtils<E>si el método no sería static. Y <E>no tiene nada que ver con el Ede ArrayUtils<E>.

Para reflejar mejor este hecho, una respuesta más correcta sería:

public static <I> I[] appendToArray(I[] array, I item)
Scheffield
fuente
30
Tenga en cuenta también que no existe absolutamente ninguna relación entre la variable de tipo de nivel de clase Ey la variable de tipo de método estático E. Considero que es una práctica mucho mejor usar un nombre de variable diferente al declarar métodos genéricos, estáticos o de otro tipo, dentro de las clases genéricas.
Juez mental
pero en este caso puedo pasar objetos de diferentes tipos a los params. Al igual que puedo pasar la matriz Integer [] como primer parámetro y elemento doble.
pinkpanther
pinkpanther: Cierto, pero no hace ningún daño, porque el método estático solo opera en un objeto de matriz que se le pasa a través de un parámetro, por lo que sus elementos seguramente tendrán el tipo correcto.
Dabbler
80
public static <E> E[] appendToArray(E[] array, E item) { ...

Tenga en cuenta el <E>.

Los métodos genéricos estáticos necesitan su propia declaración genérica ( public static <E>) separada de la declaración genérica de la clase ( public class ArrayUtils<E>).

Si el compilador se queja de una ambigüedad de tipo al invocar un método genérico estático (de nuevo no es probable en su caso, pero, en general, por si acaso), a continuación se explica cómo invocar explícitamente un método genérico estático utilizando un tipo específico ( _class_.<_generictypeparams_>_methodname_):

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

Esto solo sucedería si el compilador no puede determinar el tipo genérico porque, por ejemplo, el tipo genérico no está relacionado con los argumentos del método.

Bert F
fuente
10

Debe mover el parámetro de tipo al nivel de método para indicar que tiene un método genérico en lugar de una clase 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
fuente
1
Esto no funcionará porque no ha definido el tipo genérico E. En este caso, aún debe tener el tipo genérico <E> en la definición de clase.
George Xavier
0

Lo explicaré de una manera simple.

Los genéricos definidos a nivel de clase están completamente separados de los genéricos definidos a nivel de método (estático).

class Greet<T> {

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

Cuando vea el código anterior en cualquier lugar, tenga en cuenta que la T definida en el nivel de clase no tiene nada que ver con la T definida en el método estático. El siguiente código también es completamente válido y equivalente al código anterior.

class Greet<T> {

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

¿Por qué el método estático necesita tener sus propios genéricos separados de los de la Clase?

Esto se debe a que se puede llamar al método estático sin siquiera instanciar la Clase. Entonces, si la Clase aún no está instanciada, aún no sabemos qué es T. Esta es la razón por la cual los métodos estáticos deben tener sus propios genéricos.

Entonces, cada vez que llama al método estático,

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

JVM lo interpreta de la siguiente manera.

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

Ambos dan las mismas salidas.

Hello Bob
Hello 123
Vishnu Vivek
fuente