I am trying to create a generic class in Java that will perform operations on numbers. In the following example, addition, as follows:
public class Example <T extends Number> {
public T add(T a, T b){
return a + b;
}
}
Forgive my naivety as I am relatively new to Java Generics. This code fails to compile with the error:
The operator + is undefined for the argument type(s) T, T
I thought that with the addition of "extends Number" the code would compile. Is it possible to do this Java or will I have to create overridden methods for each Number type?
Number does not have a + operator associated with it, nor can it since there is no operator overloading.
It would be nice though.
Basically, you are asking java to autobox a descedant of Number which happens to include Integer, Float and Double, that could be autoboxed and have a plus operator applied, however, there could be any number of other unknown descendants of Number that cannot be autoboxed, and this cannot be known until runtime. (Damn erasure)
Your problem is not really related to generics, rather to operators, primitives vs objects, and autoboxing.
Think about this:
public static void main(String[] args) {
Number a = new Integer(2);
Number b = new Integer(3);
Number c = a + b;
}
The above does not compile
public static void main(String[] args) {
Integer a = new Integer(2);
Integer b = new Integer(3);
Number c = a + b;
}
The above does compile, but only because of autoboxing - which is kind of a hacky syntax glue introduced in Java 5, and only works (in compile time) with some concrete types : int-Integer for example.
Behind the scenes, the Java compiler is rewriting the last statement ("I must unbox a and b to apply the sum operator with primitive datatypes, and box the result to assign it to object c") thus:
Number c = Integer.valueOf( a.intValue() + b.intValue() );
Java can't unbox a Number because it does not know at compile time the concrete type and hence it cannot guess its primitive counterpart.
You can do something like this
class Example <T extends Number> {
public Number add(T a, T b){
return new Double(a.doubleValue() + b.doubleValue());
}
}
Yes, Nathan is correct. If you want something like this, you have to write it yourself
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));
Too bad Java has no type classes (Haskell) or implicit objects (Scala), this task would be a perfect use case...
There are similar questions to this one, and the answer is you can't do it like that.
You could check if a and b are an instance of Long/Double/Integer/etc. and delegate the add to methods like:
public Integer add(Integer a, Integer b) {
return a+b; // this actually uses auto boxing and unboxing to int
}
And you would need to create one for every type that extends Number, so that's not really feasible. In other words, don't use generics for numeric operations. Number as a superclass is pretty limited.
Consider Example<Number>, how would + work on that? There is no add or similar in Number or even the likes of Integer.
Worse consider final class FunkyNumber extends Number { ... weird stuff, no add op ... }.
Even the java runtime library has this problem, most of the methods dealing with primitives have to duplicate the same functionality.
The fastest option would be to write your code for one type and then copy it and replace the type to generate the methods for the other types. A short script should be enough to do this.
Related
I am trying to create a generic class in Java that will perform operations on numbers. In the following example, addition, as follows:
public class Example <T extends Number> {
public T add(T a, T b){
return a + b;
}
}
Forgive my naivety as I am relatively new to Java Generics. This code fails to compile with the error:
The operator + is undefined for the argument type(s) T, T
I thought that with the addition of "extends Number" the code would compile. Is it possible to do this Java or will I have to create overridden methods for each Number type?
Number does not have a + operator associated with it, nor can it since there is no operator overloading.
It would be nice though.
Basically, you are asking java to autobox a descedant of Number which happens to include Integer, Float and Double, that could be autoboxed and have a plus operator applied, however, there could be any number of other unknown descendants of Number that cannot be autoboxed, and this cannot be known until runtime. (Damn erasure)
Your problem is not really related to generics, rather to operators, primitives vs objects, and autoboxing.
Think about this:
public static void main(String[] args) {
Number a = new Integer(2);
Number b = new Integer(3);
Number c = a + b;
}
The above does not compile
public static void main(String[] args) {
Integer a = new Integer(2);
Integer b = new Integer(3);
Number c = a + b;
}
The above does compile, but only because of autoboxing - which is kind of a hacky syntax glue introduced in Java 5, and only works (in compile time) with some concrete types : int-Integer for example.
Behind the scenes, the Java compiler is rewriting the last statement ("I must unbox a and b to apply the sum operator with primitive datatypes, and box the result to assign it to object c") thus:
Number c = Integer.valueOf( a.intValue() + b.intValue() );
Java can't unbox a Number because it does not know at compile time the concrete type and hence it cannot guess its primitive counterpart.
You can do something like this
class Example <T extends Number> {
public Number add(T a, T b){
return new Double(a.doubleValue() + b.doubleValue());
}
}
Yes, Nathan is correct. If you want something like this, you have to write it yourself
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));
Too bad Java has no type classes (Haskell) or implicit objects (Scala), this task would be a perfect use case...
There are similar questions to this one, and the answer is you can't do it like that.
You could check if a and b are an instance of Long/Double/Integer/etc. and delegate the add to methods like:
public Integer add(Integer a, Integer b) {
return a+b; // this actually uses auto boxing and unboxing to int
}
And you would need to create one for every type that extends Number, so that's not really feasible. In other words, don't use generics for numeric operations. Number as a superclass is pretty limited.
Consider Example<Number>, how would + work on that? There is no add or similar in Number or even the likes of Integer.
Worse consider final class FunkyNumber extends Number { ... weird stuff, no add op ... }.
Even the java runtime library has this problem, most of the methods dealing with primitives have to duplicate the same functionality.
The fastest option would be to write your code for one type and then copy it and replace the type to generate the methods for the other types. A short script should be enough to do this.
public class GenMethodDemo{
public GenMethodDemo(){
Sum.<Integer,Integer,Integer>sum(1,2);
}
public static void main(String args[]){
new GenMethodDemo();
}
}
class Sum{
public static final <S extends Number,Z extends S,X extends S> S sum(Z v1,X v2){
System.out.printf("v1=%1$s,v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
Error get:
error: bad operand types for binary operator '+'
return v1+v2;
first type: Z
second type: X
where Z,S,X are type-variables:
Z extends S declared in method <S,Z,X>sum(Z,X)
S extends Number declared in method <S,Z,X>sum(Z,X)
X extends S declared in method <S,Z,X>sum(Z,X)
1 error
Can't understand what i'm doing wrong? If i change S.Z.X with Integer - all works fine but why with generics code won't compile?
Refactored code to:
public class GenMethodDemo2{
public GenMethodDemo2(){
Sum.<Integer>sum(1,2);
}
public static void main(String args[]){
new GenMethodDemo2();
}
}
class Sum{
public static final <S extends Integer> S sum(S v1,S v2){
System.out.printf("v1=%1$s, v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
}
error: incompatible types: int cannot be converted to S
return v1+v2;
where S is a type-variable:
S extends Integer declared in method <S>sum(S,S)
1 error
So, S supposed to be an Integer or any subclass of Integer class, in any way it definitely should be possible to + their values. What's wrong with this version?
S extends Integer but int cannot be converted to S, how it could be? Why there is no autoboxing?
The problem you're experiencing is because there is no + operator defined for Number, only specific subclasses of Number. For example, + is defined for Integer, Double etc, but not BigInteger, BigDecimal or any other non-standard implementation of Number.
There is no good way to do generic addition. You end up having to provide a BinaryOperator<S>, so your code looks like:
sum(1, 2, Integer::sum);
sum(1.0, 2.0, Double::sum);
which is more verbose than just:
1 + 2
1.0 + 2.0
The compiler requires + to be defined for the compile-time types of v1 and v2. It doesn't matter if they are Integer (or whatever) at runtime: the decision as to whether to allow the + is made by the compiler, because it has to be able to guarantee that the method is type-safe for any arguments.
The method above is compiled to this:
public static final Number sum(Number v1, Number v2){
System.out.printf("v1=%1$s,v2=%2$s%n",v1.getClass(),v2.getClass());
return v1+v2;
}
This is called type erasure.
If + isn't defined for a Number, this code isn't allowed.
As a general solution for all inbuilt Number extensions:
public static Number sum(final Number a, final Number b) {
return new BigDecimal(a.toString()).add(new BigDecimal(b.toString()));
}
(Note: there's no guarantee that toString() will give a String which is parseable by BigDecimal but it does for all the inbuilt JDK Number extensions to the best of my knowledge.)
If you wanted to do something more clever, you could do some checks with instanceof to find the types of the inputs and work from there, but I tried that once for implementing Comparable between all Numbers and the performance wasn't any better than casting to BigDecimal.
Say we got 2 ordered collections of numbers. We want to compute the arithmetic difference element by element.
I think we need to use List of Number in order to model the idea of 'ordered collection of numbers'.
The problem is that the arithmetic difference (a banal '-' like in 3-2) is not defined for Number.
I can cast everything to Double, but I'd rather prefer a clean solution.
public static <E extends Number> List<E> listDifferenceElementByElement(List<E> minuend, List<E> subtrahend) throws Exception {
if (minuend.size() != subtrahend.size()) {
throw new Exception("Collections must have the same size"); //TODO: better exception handling
}
List<E> difference = new ArrayList<E>();
int i = 0;
for (E currMinuend : minuend) {
difference.add(currMinuend-subtrahend.get(i)); //error: The operator - is undefined for the argument type(s) E, E
i++;
}
return difference;
}
Any idea?
Since Number does not provide any subtract method, you can't do it simply. The alternatives I can think of are:
use the provided doubleValue() method, and return a double, but you could lose precision if the lists contain BigDecimal for example.
have one method per available Number subclass, but it could create a lot of duplicated code...
restrict the type to whatever makes sense (say double if precision does not matter too much or BigDecimal if it does), which is a subcase of (2).
Option 2 could be implemented like by replacing difference.add(currMinuend-subtrahend.get(i)); by difference.add(subtract(currMinuend, subtrahend.get(i)));
Then it's only a matter of writing 10 subtract methods:
private static int subtract(int a, int b) { return a - b; }
private static double subtract(double a, double b) { return a - b; }
private static BigDecimal subtract(BigDecimal a, BigDecimal b) { return a.subtract(b); }
etc.
This is nothing to do with generics, arrays, etc. Java doesn't allow operator overloading, so you cannot subtract 2 numbers with -, and the Number class doesn't define any methods for numeric operations. It can't because it might be operating on different subclasses (e.g. Integer.subtract(Double)) which would make defining the return type impossible.
You will need to use intrinsic types or BigDecimal (or similar) here, I think.
If you want to work with many arithmetic type, you can set your list as a generic list, then add the handler when you want to find the difference between them. The handler check if the type is instance of Double, Integer, or anything you want. Based on each type, you could compute the difference.
for a long while, i have been trying to get a piece of code to work, and it just is not possible. the class i have requires a generic which i have set to Integer. so i tried the follwoing:
public class a<T> //set generics for this function
{
private T A;
protected boolean d;
public a(final T A)
{
this.A = A;
//do calculations
//since constructor cannot return a value, pass to another function.
this.d = retnData(Integer.parseInt(A.toString()) != 100); //convert to an integer, the backwards way.
}
private boolean retnData(boolean test)
{
return test;
}
}
// IN ANOTHER CLASS
transient a<Integer> b;
boolean c = b.d = b.a(25); // throws an erorr: (Int) is not apporpriate for a<Integer>.a
Java will not allow this since java sees that int != Integer, even though both accept the same data. and because of the way generics works i cannot set a b; because of the primitiveness of the type int. Even casting to an Integer does not work, as it still throws the "type error"
finnaly, i have to ask if there is some sort of work around for this, or is it futile to try and get this to work?
You are trying to explicitly call a constructor as an instance method. This cannot be done in Java.
Perhaps you want:
transient a<Integer> b = new a<Integer>(25);
boolean c = b.d;
However, since d is declared to be protected, that will only work if this code is in another class derived from a or in the same package.
Use
final a<Integer> b = new a<Integer>(10);
boolean c = b.d;
int can be explicitly converted to Integer with new Integer(10) or Integer.valueOf(10)
The code does not make much sense: b is an object of type a, which does not have an a method - so not sure what you expect from b.a(25);... This has nothing to do with int vs Integer...
I need help with a problem I have with generics in Java. I'm writing this Computer algebra system, where the user enters a math expression and the system works with it in different ways (expand it, simplify it etc). It worked fine for expressions containing natural numbers, and I wanted to expand it to work with mathematical sets as well. Instead of +, you would have the intersection operator, etc.
At first, I started recording everything for the sets, but then I realized this was probably not good and started using generics.
Instead of having one parse tree like MathExpr and one like SetExpr, I thought I could just have a generic Expression<T> and build a base class Number and a base class Set.
To try to clarify, I want a mathematical expression like (2 * a) + (3 + 2) to be an instance of a class Expression<Number> and a set expression like (A ∪ B) ∩ C to be an instance of Expression<Set>. I can then perform different operations on this, like calculate the depth etc.
The + operation is implemented as one class, the * as one class etc. Both these classes are subclasses of an abstract class called TwoExpr which in turn is a subclass of the abstract class Expr. This is how I have done it now and everything works fine.
When I wanted to change my code I made my Expr class generic. That is Expr<T>. I also changed TwoExpr to TwoExpr<T> and created a base class Number.
The problem is, now I can't seem to instantiate objects of the type Sum<Number>.
I get a "Type mismatch: cannot convert from Sum to Expr<Number>" error. But Sum is a subclass of TwoExpr<Number>, which in turn is a subclass of Expr<Number>. As you may realize, I can't make the class Sum generic and call it Sum<Number>, because all arithmetic operations don't have analogues for sets.
I have always been able to create objects like
Expr zero= new Leaf(0);
Variable a = new Variable("a");
Expr aPlusZero = new Sum(a, zero);
When I changed to generics, the same code looks like this:
Expr<Number> zero= new Leaf<Number>(new Number(0)); //works fine
Variable<Number> a = new Variable<Number>("a"); //works fine
Expr<Number> APlusZero=new Sum(a,zero); //gives a "Type mismatch:
//cannot convert from Sum to Expr<Number>" error
How come it doesn't recognize that Sum(a,zero) is a subclass of Expr<Number>, when it says in the declaration of Sum
public class Sum extends TwoExpr<Number> {
public Sum(Expr<Number> a, Expr<Number> b) {
super(a, b);
}
...
}
and in the declaration of TwoExpr
public abstract class TwoExpr<T> extends Expr<T> {
protected Expr<T> a;
protected Expr<T> b;
public TwoExpr(Expr<T> a, Expr<T> b) {
this.a=a;
this.b=b;
}
...
}
I know that Lizkows substitution principle doesn’t apply for generic arguments. But Number isn’t a subclass of anything (except Object) and don't have any subclasses.
I hope I've been fairly clear about what I'm trying to do and what problem I have. Does anybody have any idea how to solve it? Please tell me if anything was unclear in the above or if you want more code.
Thanks in advance.
Mattias
I think your problem is in the classes you did not show, I tried the following and it works:
Expr<Number> zero= new Expr<Number>();
Expr<Number> a= new Expr<Number>();
Expr<Number> APlusZero=new Sum(a,zero);
Might it be that Variable is not an Expr?
UPDATE:
I played a little creating Variable and Leaf as I imagine them and it all works:
public class Number {
public Number(int i){}
}
public class Variable<T> extends Expr<T> {
public Variable(String s){}
}
public class Leaf<T> extends Expr<T> {
public Leaf(T t) {
super();
}
}
public class Expr<T> {
}
public class TwoExpr<T> extends Expr<T> {
public TwoExpr(Expr<T> a, Expr<T> b) {
}
}
public class Sum extends TwoExpr<Number> {
public Sum(Expr<Number> a, Expr<Number> b) {
super(a, b);
}
}
public class AllTogether {
public static void main(String[] args) {
Expr<Number> zero= new Leaf<Number>(new Number(0));
Variable<Number> a = new Variable<Number>("a");
Expr<Number> APlusZero=new Sum(a,zero);
}
}
If you take the extends Expr from Variable it does give the error you experience, might it be this the cause?
Maybe try to debug it:
Object = new Sum(a,zero);
System.out.println(o.getClass().getGenericSuperclass());
Furthermore maybe is a better solution for your system.
Sum does not import Types.Number. So it's not Expr<Types.Number> but Expr<java.lang.Number>. I would assume this would give a compilation error not only on the assignment but also on the construction of new Sum(vA, zero), but maybe the compiler sees the other error first.