Java - сигнатура метода общего числа, привязанная к определенным подклассам - PullRequest
0 голосов
/ 07 июня 2018

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

public <N1 extends Number, N2 extends Number> N2 op(N1 num, Class<N2> retType) {
    //Sample operation, where type conflicts arise.
    return retType.cast(num + 3);
}

Конечно, я не могу просто разыграть N1 на N2, если они, скажем, Integer и Long.Тем не менее, Number предлагает doubleValue(), intValue(), ..., методы, которые могут позволить мне частично обойти проблему с помощью оператора switch / case.Это ограничило бы меня типами возврата xxxValue() методов, которые предоставляет класс Number, таким образом, обрезая AtomicInteger, AtomicLong, BigInteger и BigDecimal.

Это все еще приемлемо для приложения, которое я имею в виду, но мои методы не смогут должным образом обрабатывать любые пользовательские или будущие официальные расширения класса Number, если не использовать оператор default вблок switch / case, который должен произвольно решить, какой метод xxxValue() вызывать, или вызвать исключение (которого я хотел бы избежать).Я мог бы использовать перечисление для инкапсуляции параметров по типу, но я боюсь, что мой код получился бы слишком запутанным и хитрым для использования.

Ответ на на этот вопрос дает дополнительное понимание относительно объявленияодин универсальный тип для двух параметров (он не гарантирует, что оба параметра на самом деле будут одного и того же типа во время выполнения), что, безусловно, стоит упомянуть здесь.

То, чего я хочу достичь, это:

  • Объявите метод с некоторыми универсальными (в широком смысле) Number параметрами, возможно, с большим количеством параметров с другим числом параметров (например, метод с двумя, метод стри параметра), но все еще являются общими для каждого из них.
  • Параметры должны быть ограничены некоторыми известными расширениями Number (например, Integer, Double).
  • параметры могут входить в несколько комбинаций, потенциально любой из (Double, Integer), (Double, Double), (Integer, Double), (Integer, Integer).

Я не хотел бы определять несколько методов с разными сигнатурами.Даже при фиксированном типе возвращаемого значения, например, Double, число методов будет увеличиваться по мере добавления аргументов и типов.

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

1 Ответ

0 голосов
/ 09 июня 2018

Вот мой лучший обходной путь, который вызывает конструктор, который принимает String в качестве параметра и возвращает ноль в случае сбоя (отредактировано для удаления printStackTrace и удаления одной ненужной ветви)

public static <R extends Number> R op(Number num, Class<R> retType) {
    BigDecimal three = BigDecimal.valueOf(3);
    BigDecimal bdNum = new BigDecimal(num.toString());

    //add three
    BigDecimal bdResult = bdNum.add(three);

    String strResult = bdResult.toString();
    Constructor[] cons = retType.getDeclaredConstructors();
    for (Constructor con: cons) {
        if (con.getParameterCount() == 1) {
            if (con.getGenericParameterTypes()[0] == String.class) {
                try {
                    return (R)con.newInstance(strResult);
                } catch (InstantiationException | IllegalAccessException | NumberFormatException e) {
                } catch (InvocationTargetException e) {
                    //if here then either the decimal place is causing a problem
                    // when converting to integral type,
                    // or the value is too large for the target type

                    // so let's try to remove the decimal point by truncating it.
                    strResult = bdResult.toBigInteger().toString();
                    try {
                        return (R)con.newInstance(strResult);
                    } catch (NumberFormatException | IllegalAccessException | InstantiationException | InvocationTargetException e1) {
                    }
                }
                //if here, then the most likely the integral type is too large
                //like trying to put 3,000,000,000 into an int
                // when largest int possible is 2,147,483,647
            }
            //if here, then no constructors with 1 String parameter
        }
    }

    return null;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...