Подтвердить что ты не робот

Java Generics и номера

В попытке увидеть, могу ли я очистить некоторые из моих математических кодов, в основном матричные вещи, я пытаюсь использовать некоторые Java Generics. У меня есть следующий метод:

private <T> T[][] zeroMatrix(int row, int col) {
    T[][] retVal = (T[][])new Object[row][col];
    for(int i = row; i < row; i++) {
        for(int j = col; j < col; j++) {
            retVal[i][j] = 0;
        }
    }
    return retVal;
}

Линия retVal [i] [j] = 0 является той, которая вызывает у меня головные боли. Целью строки является инициализация массива с представлением T из 0. Я пытался делать с ним всевозможные вещи: (T определяется в классе как T extends Number)

retVal[i][j] = (T)0;
retVal[i][j] = new T(0);

Единственное, что работает, это

retVal[i][j] = (T)new Object(0);

Это не то, что я хочу.

Возможно ли это? Есть ли более простой способ представления матрицы NxM любого типа Number (включая потенциально BigDecimal), или я застрял?

4b9b3361

Ответ 1

<T extends Number> T[][] zeroMatrix(Class<? extends Number> of, int row, int col) {
    T[][] matrix = (T[][]) java.lang.reflect.Array.newInstance(of, row, col);
    T zero = (T) of.getConstructor(String.class).newInstance("0");
    // not handling exception      

    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; 
            matrix[i][j] = zero;
        }
    }

    return matrix;
}

использование:

    BigInteger[][] bigIntegerMatrix = zeroMatrix(BigInteger.class, 3, 3);
    Integer[][] integerMatrix = zeroMatrix(Integer.class, 3, 3);
    Float[][] floatMatrix = zeroMatrix(Float.class, 3, 3);
    String[][] error = zeroMatrix(String.class, 3, 3); // <--- compile time error
    System.out.println(Arrays.deepToString(bigIntegerMatrix));
    System.out.println(Arrays.deepToString(integerMatrix));
    System.out.println(Arrays.deepToString(floatMatrix));

ИЗМЕНИТЬ

общая матрица:

public static <T> T[][] fillMatrix(Object fill, int row, int col) {
    T[][] matrix = (T[][]) Array.newInstance(fill.getClass(), row, col);

    for (int i = 0; i < row; i++) {
        for (int j = 0; j < col; j++) {
            matrix[i][j] = (T) fill;
        }
    }

    return matrix;
}    

Integer[][] zeroMatrix = fillMatrix(0, 3, 3); // a zero-filled 3x3 matrix
String[][] stringMatrix = fillMatrix("B", 2, 2); // a B-filled 2x2 matrix

Ответ 2

Массивы и дженерики не работают хорошо:

"Массивы являются ковариантными, что означает, что массив ссылок на супертипы является супертипом массива ссылок на подтипы. То есть Object[] является супертипом String[], а массив строк можно получить через ссылочную переменную типа Object[]."

см. Часто задаваемые вопросы Java Generics:

Ответ 3

он должен быть нулевым, а не нулем.

Если вы хотите фактически разместить там эквивалент 0 для объекта T, вам нужно предоставить factory T. Что-то вроде этого:

interface Factory<T> {
   T getZero();     
}

и вы должны сделать метод следующим образом:

private <T> T[][] zeroMatrix(int row, int col, Factory<T> factory) {
    T[][] retVal = (T[][])new Object[row][col];
    for(int i = row; i < row; i++) {
        for(int j = col; j < col; j++) {
            retVal[i][j] = factory.getZero();
        }
    }

    return retVal;
}

У вас также должны быть правильные реализации для factory:

 class IntegerFactory implements Factory<Integer> {
    Integer getZero() {
       return new Integer(0);
    }
}

Обычно вы помещаете getMatrix(int row, int column) в реализацию factory, чтобы фактически вернуть правильный типизированный массив.

Ответ 4

В Java тип стирается во время выполнения, поэтому вам нужно передать другой аргумент, чтобы получить тип во время выполнения.

Это может быть либо значение для инициализации массивов, либо класс для использования.

Если вы решите пройти в классе, тогда у вас есть карта класса для значения для хранения нулевого значения для каждого типа.

Затем вы можете использовать java.util.Arrays.fill для заполнения массива:

private static HashMap<Class<?>, Object> ZEROS = new HashMap<Class<?>,Object>();

static {
    ZEROS.put( Integer.class, Integer.valueOf(0) );
    ...
}

private static <T extends Number> T[][] zeroMatrix ( Class<T> type, int rows, int cols ) {
    @SuppressWarnings("unchecked")
    T[][]   matrix  = (T[][]) java.lang.reflect.Array.newInstance(type, rows, cols);
    Object  zero    = ZEROS.get(type);

    for ( T[] row : matrix ) 
        java.util.Arrays.fill(row,zero);

    return matrix;
}

Integer[][] matrix = zeroMatrix (Integer.class, 10, 10);

Однако, если производительность отдаленно связана с тем, что вы не хотите использовать вложенные значения для числового кода.

Вы действительно не хотите пытаться использовать нуль для нуля - это вызовет сложность всех других путей в вашем коде. Хотя вы можете уйти с числовым классом поддержки, который обеспечит добавление и умножение различных типов номеров в коробке, объем сложности, который вы сохраните, будет очень мало по сравнению с предоставлением двух или трех примитивных матриц и нескольких больших чисел, особенно если вы используете систему шаблонов (например, ant заменить задачу или XSLT) для генерации исходного кода.

Ответ 5

Ye olde (reference) массивы плохо воспроизводятся с дженериками. В этом случае массивы также, вероятно, будут неэффективными. Вы создаете массив массивов, поэтому есть ненужная проверка косвенности и границ. Лучше сделать класс Matrix<T>. Вы также можете добавить к Matrix ссылку на экземпляр T, который представляет нуль.

Ответ 6

Генерики и массивы не очень хорошо сочетаются. Создание универсального массива не допускается, так как оно не будет типичным. Это связано с тем, что если Sub является подтипом Super, то Sub [] является подтипом Super [], что не относится к родовым типам; для любых двух разных типов Type1 и Type2, List не является ни подтипом, ни супертипом List. (Эффективная Java охватывает это в главе 5, пункт 25).

Ответ 7

Я думаю, ты сражаешься с проигравшей битвой. Даже если вы решите это, как вы планируете решение сложения, вычитания и т.д.? Класс числа не очень полезный суперкласс, и единственный полезный метод - doubleValue().

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

Если вы хотите этого, вам может быть лучше просто придерживаться BigDecimal для всего, но, конечно, это будет связано со штрафами за производительность.

Другой очевидный вариант заключается в том, чтобы оставить массив с нулевой инициализацией, а затем изменить другой код для обработки нулевого значения.

Ответ 8

Вы должны учитывать, что генерические средства используются только во время компиляции для проверки безопасности типов. Эта информация теряется во время выполнения, поэтому вы не можете использовать авто-бокс на retVal [i] [j] = 0; поскольку Java не может автоматическое поле ввода Number или Object.

Если вы передадите значение, которое вы хотите установить, оно будет работать. Вот пример:

private <T> T[][] fillMatrix(int row, int col, T value) {
    T[][] retVal = (T[][])new Object[row][col];
    for(int i = 0; i < row; i++) {
       for(int j = 0; j < col; j++) {
          retVal[i][j] = value;
       }
    }
    return retVal;
}

Btw, for (int я = row; я < row; я ++), и для (int j = col; j < col; j ++) никогда не будет циклически, поэтому возникнет другая проблема с вашим кодом.

изменить: Вы не сможете применить результат к чему-то другому, кроме Object [] [], хотя из-за того, что это фактический тип массива.

Ответ 9

Если вы действительно хотите использовать generics, вы можете сделать что-то вроде этого

private <T extends Number> T[][] zeroMatrix(int row, int col, Class<T> clazz) throws InstantiationException, IllegalAccessException,
        IllegalArgumentException, InvocationTargetException
{
    T[][] retVal = (T[][]) Array.newInstance(clazz, new int[] { row, col });
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            Constructor<T> c = clazz.getDeclaredConstructors()[0];
            retVal[i][j] = c.newInstance("0");
        }
    }

    return retVal;
}

Пример:

zeroMatrix(12, 12, Integer.class);

Ответ 10

Я поднял вопрос который также задал вопрос о проблемах с производительностью, который ссылался на ваш вопрос. Консенсус был ясен, что рефакторинг для дженериков имел значительный успех и поэтому вы должны придерживаться примитивов, если это имеет значение (для меня это так).

Ответ 11

Использование стирания Java для реализации дженериков означает, что у вас возникнут проблемы с новым типичным типом.

Как насчет использования значения null для представления 0

retVal[i][j] = null;

Затем вы можете назначить любой тип, который требуется для массива, позже.