Java generic и типы массивов, не то, что вы думаете (например, массивы из общих типов)

Я пытаюсь указать, что общий класс должен быть массивом или, еще лучше, примитивным массивом. Пока это то, что я получил:

interface Foo<T> {
  void process( T data );
}

public class Moo implements Foo<int[]> {
  void process( int[] data ) {
     // do stuff here
  }
}

Это вполне корректный Java-код и работает, потому что примитивные массивы расширяют Object. Обратите внимание, что этот вопрос полностью отличается от всех других общих вопросов Java, которые я продолжаю находить. В этой ситуации люди хотят создать массив из общего типа.

Проблема заключается в том, что тип T может быть тем, что расширяет Object. Я хочу сделать что-то вроде:

<T extends ONLY ARRAYS>

или

<T extends ONLY PRIMITIVE ARRAYS>.

Возможно ли это?

EDIT: конечной целью было бы добавить проверку времени компиляции в передаваемом массиве. Прямо сейчас любой старый объект может быть передан, и он будет компилироваться просто отлично. Ошибка будет обнаружена только во время выполнения, когда выбрано исключение класса. На самом деле это весь смысл Generics в Java, чтобы добавить более сильную проверку типа времени компиляции.

Ответы

Ответ 1

Вы не можете этого сделать. Никакой класс не может расширять массив, поэтому никогда не будет типа, который удовлетворяет общему параметру T extends Object[]. (Кроме Object[], но тогда вы не будете использовать generics.)

Что вы можете сделать, это примерно так:

public interface Foo<T extends Number> {
    public void process(T[] data);
}

Но тогда вы можете столкнуться с проблемами производительности с боксом.

Ответ 2

В Java параметры типа могут only быть ограничены отношением подтипа и единственными распространенными супертипами всех массивов Object, Clonable и Serializable. Самое близкое, что вы можете получить, это ограничить Object[], который является супертипом всех массивов с не примитивными типами компонентов или, возможно, до Number[], который является супертипом Integer[], Long[],...

Даже если Java поддерживает такое ограничение, как бы вы сделали что-нибудь полезное с этим массивом? Вы не можете читать отдельные элементы, так как вы не можете объявить переменную для хранения результатов и не писать отдельные элементы, поскольку вы не можете записать выражение, которое присваивается элементу массива.

Тем не менее, я бы привязал переменную типа к типу компонента, а не к типу массива:

interface Foo<T extends Whatever> {
    void process(T[] data );
}

потому что вы можете обратиться к T[], зная T, но зная, что T extends Object[] не позволяет напрямую ссылаться на тип компонента.

Изменить: Джеффри правильно указывает, что тип массива не может использоваться в ограничениях типа, т.е. <T extends Whatever[]> не компилируется, поэтому вам обязательно нужно следовать моему совету объявления <T extends Whatever> и использовать T[] для ссылки на тип массива.

Ответ 3

Нет. Нет "интересного" интерфейса и никакого супертипа, но Object существует для массивов примитивов:

public class SOTEST {    
    public static void main(String[] args) throws Exception {
        int[] arr = new int[] {};
        Class c = arr.getClass();
        for(Class clazz:c.getInterfaces()) {
            System.out.println(clazz.getName());
        }

        System.out.println(c.getSuperclass().toString());
    }
}

Ответ 4

Массивы X не имеют иерархии типов. Integer [] не является подклассом Number [].

Чтобы получить то, что вы хотите, используйте тип компонента массива в качестве вашего параметра T и объявите параметры и возвращайте типы как массивы T.