Могу ли я выполнять арифметические операции над базовым классом Number?
Я пытаюсь создать общий класс в Java, который будет выполнять операции с числами. В следующем примере добавим следующее:
public class Example <T extends Number> {
public T add(T a, T b){
return a + b;
}
}
Простите мою наивность, поскольку я относительно новичок в Java Generics. Этот код не скомпилируется с ошибкой:
Оператор + является undefined для типа аргумента (s) T, T
Я думал, что с добавлением "extends Number" код будет компилироваться. Возможно ли это сделать для Java или мне придется создавать переопределенные методы для каждого типа номера?
Ответы
Ответ 1
Число не имеет связанного с ним оператора +, а также не может быть, поскольку нет перегрузки оператора.
Было бы неплохо, однако.
В принципе, вы запрашиваете java для autobox - дескриптор номера, который включает в себя Integer, Float и Double, который может быть автобоксирован и иметь примененный оператор плюс, однако может существовать любое количество других неизвестных потомков Number, не может быть автобоксированным, и это невозможно узнать до выполнения. (Проклятое стирание)
Ответ 2
Ваша проблема не связана с дженериками, а не с операторами, примитивами и объектами, а также с автобоксированием.
Подумайте об этом:
public static void main(String[] args) {
Number a = new Integer(2);
Number b = new Integer(3);
Number c = a + b;
}
Вышеописанное не компилирует
public static void main(String[] args) {
Integer a = new Integer(2);
Integer b = new Integer(3);
Number c = a + b;
}
Вышеприведенное компилируется, но только из-за автобоксинга - своего рода хакерский синтаксический клей, введенный в Java 5, и работает только (во время компиляции) с некоторыми конкретными типами: например, int-Integer.
За кулисами компилятор Java переписывает последний оператор ( "Я должен распаковать a
и b
, чтобы применить оператор sum с примитивными типами данных и поместить результат, чтобы назначить его объекту c
" ) таким образом:
Number c = Integer.valueOf( a.intValue() + b.intValue() );
Java не может распаковать a Number
, потому что во время компиляции он не знает конкретный тип и, следовательно, не может угадать его примитивную копию.
Ответ 3
Вы можете сделать что-то вроде этого
class Example <T extends Number> {
public Number add(T a, T b){
return new Double(a.doubleValue() + b.doubleValue());
}
}
Ответ 4
Да, Натан прав. Если вы хотите что-то вроде этого, вы должны написать это самостоятельно
public class Example <T extends Number> {
private final Calculator<T> calc;
public Example(Calculator<T> calc) {
this.calc = calc;
}
public T add(T a, T b){
return calc.add(a,b);
}
}
public interface Calculator<T extends Number> {
public T add(T a, T b);
}
public class IntCalc implements Calculator<Integer> {
public final static IntCalc INSTANCE = new IntCalc();
private IntCalc(){}
public Integer add(Integer a, Integer b) { return a + b; }
}
...
Example<Integer> ex = new Example<Integer>(IntCalc.INSTANCE);
System.out.println(ex.add(12,13));
Слишком плохо Java не имеет классов классов (Haskell) или неявных объектов (Scala), эта задача будет идеальным вариантом использования...
Ответ 5
Есть похожие вопросы к этому, и ответ заключается в том, что вы не можете сделать это так.
Вы можете проверить, являются ли a и b экземпляром Long/Double/Integer/etc. и делегировать добавление к таким методам, как:
public Integer add(Integer a, Integer b) {
return a+b; // this actually uses auto boxing and unboxing to int
}
И вам нужно будет создать один для каждого типа, который расширяет число, так что это не реально. Другими словами, не используйте generics для числовых операций. Номер как суперкласс довольно ограничен.
Ответ 6
Рассмотрим Example<Number>
, как бы +
работать над этим? Существует не add
или подобное в Number
или даже таких, как Integer
.
Хуже рассмотреть final class FunkyNumber extends Number { ... weird stuff, no add op ... }
.
Ответ 7
Даже в библиотеке времени исполнения Java есть эта проблема, большинство методов, связанных с примитивами, должны дублировать ту же функциональность.
Самый быстрый вариант - записать код для одного типа, а затем скопировать его и заменить тип, чтобы сгенерировать методы для других типов. Для этого достаточно короткого script.