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.
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.
sorry, this question might be very simple but I didn't find the answer in the internet.
public class Main {
public static void main(String[] args) {
int a = 1;
double b=2;
max(a,b);
}
public static <E> void max(E first , E second)
{
System.out.println(first);
System.out.println(second);
}}
when we pas the first parameter an integer then E is set to Integer and then we pass a double to it. we should get a compile error. (because E is Integer) but the program runs correctly and the output is
1
2.0
so what is my mistake?
If you hover over the method call in Eclipse, you'll see:
<? extends Number> void Main.max(? extends Number first, ? extends Number second)
i.e. the compiler infers the type of the generic type parameter as something that extends Number. Therefore, both Integer and Double, which extend Number are valid arguments for the max method.
If you print the types of the arguments passed to the max method:
public static <E> void max(E first , E second)
{
System.out.println(first);
System.out.println(second);
System.out.println (first.getClass ());
System.out.println (second.getClass ());
}
you'll see that an Integer and a Double were passed to the method:
1
2.0
class java.lang.Integer
class java.lang.Double
Java's type inference algorithm finds the most specific type that your arguments share. In this case, it should be Number.
Based on my understanding, there are two things. one is generic class and other one is generic method. In both the cases, you can pass any type of value (regardless of type) you can pass any type of parameter. Now when you are creating an object of specific generic lass type like MyClass<Integer> its no more generic, you can expect compiler error while doing operation with different type of parameter. But when you have some method like which adds element to a List<E>, you can add anything to this list, you will not get any compilation error.
public class ClassGenericParameterized<T> {
public void add(T t1, T t2){
//System.out.println(t1+t2);
System.out.println(Integer.parseInt(t1.toString()) + Integer.parseInt(t2.toString()));
}
public void multiply(T t1, T t2){
System.out.println(Integer.parseInt(t1.toString()) * Integer.parseInt(t2.toString()));
}
public void subtract(T t1, T t2){
System.out.println(Integer.parseInt(t1.toString()) - Integer.parseInt(t2.toString()));
}
public void division(T t1, T t2){
System.out.println(Integer.parseInt(t1.toString()) / Integer.parseInt(t2.toString()));
}
public static void main(String[] args) {
ClassGenericParameterized<Integer> ob = new ClassGenericParameterized<Integer>();
ob.add(1,2);
ob.multiply(2, 4);
ob.subtract(15, 6);
ob.division(6, 3);
}
}
What is the logic and need of making the class Generic in java
when replace the statement
ClassGenericParameterized<Integer> ob = new ClassGenericParameterized<Integer>()
by
ClassGenericParameterized<Double> ob = new ClassGenericParameterized<Double>()
gives
Error(The method add(Double, Double) in the type ClassGenericParameterized<Double> is not applicable for the arguments (int, int))
As my thinking it is because of the statement written as inside the add method as Integer.parseInt(t1.toString()) + Integer.parseInt(t2.toString())
so again tried to replace the statement
System.out.println(Integer.parseInt(t1.toString()) + Integer.parseInt(t2.toString()));
by
System.out.println(t1+t2)
which gives
Error(The operator + is undefined for the argument type(s) T, T).
Then how could this made to be generic in real sense ? Could someone please explain why it is happening and how to rectify this error to implement my functionality?
In this scenario, generics would not be a good fit. The reason:
You cannot simply declare any type capable of addition, subtraction, multiplication, or division. T has an upper bound of Object.
If you set the bound to <T extends Number>, this still wouldn't work, since you couldn't guarantee that you could autobox whatever you wanted in that parameter to the type T the class was bound to.
Ultimately, you're going to want to look at using Number instead as your parameter. Since Number is the parent class of all numerical wrappers including BigInteger and BigDecimal, you can leverage that instead.
public class NumberOperations {
public void add(Number t1, Number t2){
//System.out.println(t1+t2);
System.out.println(Integer.parseInt(t1.toString()) + Integer.parseInt(t2.toString()));
}
public void multiply(Number t1, Number t2){
System.out.println(Integer.parseInt(t1.toString()) * Integer.parseInt(t2.toString()));
}
public void subtract(Number t1, Number t2){
System.out.println(Integer.parseInt(t1.toString()) - Integer.parseInt(t2.toString()));
}
public void division(Number t1, Number t2){
System.out.println(Integer.parseInt(t1.toString()) / Integer.parseInt(t2.toString()));
}
public static void main(String[] args) {
NumberOperations ob = new NumberOperations();
ob.add(1, 2);
ob.multiply(2, 4);
ob.subtract(15, 6);
ob.division(6, 3);
}
}
A little of theory first:
Generics in Java (and other languages) is a language technique oriented to use the same program to process fully disjoint types.
The most common example are the Collection containers: Any Collection stores objects of "any" parameter type, which is unknown at compile time but will be always the same at runtime. For example:
public class MyClass<T>
{
private T[] array;
public void set(T t) {...}
public T get() {...}
public T[] get2() {...}
public Set<T> get3() {...}
}
When compiling this class, we don't know what will be T. But with this client program:
MyClass<String> obj=new MyClass<String>();
... We will have an object of MyClass which will be bounded always to String: Every ocurrence of T will be filled with String.
If you want to ensure some hierarchy constraint in your generic class, you can require that the parameter type has a minimal superclass like this:
public class MyClass<T extends SomeOtherClass>
Now, let's go to the practical problem:
Your abstraction is well designed but wrongly used: In the main method, you are suplying int values. That's okay for Integer type parameter, but be careful: If you change to Double type parameter, you must also change the values provided as input parameters for the methods.
Altough is well designed, it is not correctly implemented: The way you convert the T values to integers is poor: One client program could declare:
ClassGenericParameterized<String> s=new ClassGenericParameterized<String>();
s.add("hello", "world");
... and the compiler would pass it as OK, but at runtime you will get a NumberFormatException, which is not want you want.
Think of it: If you want to code an abstraction that is oriented only to numeric values, you should rely on the compiler to check that only numeric types are provided as type parameters: Make T extend Number:
public class MyClass<T extends Number>
... and in this way, you could take advantage of the intValue() method to convert them to ints. And, most important, you won't need to use insecure parse* methods which could throw unpredictable exceptions at runtime.
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.
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.