Can someone tell me why this code gives me a compile-error?
public class Main {
public static void main(String[] args) {
System.out.println(sum(2, 6.9));
}
public static <T extends Number<T>> T sum(T a, T b) {
T result = a + b; // compile-error here
return result;
}
}
Number is not a generic class, so you can't parameterize it:
public abstract class Number implements java.io.Serializable {
...
}
Further, the + operator only works on primitive types like int, long, etc., rather than Number subtypes like Integer, Long, etc. (EDIT: It will operate on these by unboxing, yes, but it cannot automatically box the result in the appropriate wrapper class.)
(You've stumbled upon one of the reasons Number is a poor example of polymorphism. It really only performs object-to-primitive conversions.)
You can, instead create an interface with
public interface ALU <T extends Number> {
public T add(T a, T b);
}
And make your main class implementing the interface created.
public class Main implements ALU <Integer>.
And create the method add inside your main class.
public Integer add(Integer a, Integer b){
return a + b;
}
And this will work.
The error The operator + is undefined for the argument type(s) T, T is caused because The type Number is not generic; it cannot be parameterized with arguments <T>
The error highlighting in eclipse provides this information, not sure if other IDE's provide the same information.
You have to use:
a.doubleValue()+b.doubleValue()
as Number is a class and does not support the operator +
Related
Consider the following code:
import java.math.BigDecimal;
class Scratch {
public static class Test<N extends Number> {
public void foo(Class<N> numberClass) {
System.out.println(numberClass);
}
public void bar() {
foo(BigDecimal.class);
}
}
public static void main(String[] args) {
Test<Number> t = new Test<>();
t.bar();
}
}
This fails to compile on line 12 (the call to foo()) with incompatible types: java.lang.Class<java.math.BigDecimal> cannot be converted to java.lang.Class<N>. I don't get it, because the generic N extends Number, and so I should be able to pass BigDecimal.class to a method which takes a Class<N> parameter. TIA for any thoughts!
Suppose a caller did:
var t = new Test<Integer>();
t.bar();
then, bar() would pass a BigDecimal to foo(), which expects an Integer.
The short answer really is that BigDecimal is not necessarily N. It is a little confusing because BigDecimal is within the <N extends Number> bounds, but the current instance's generic type argument can be any type that extends Number, but the bar method assumes BigDecimal.
Check this slightly modified version of the method:
public void bar() {
new Test<BigDecimal>().foo(BigDecimal.class);
}
That method compiles. Why? Becuase Test<BigDecimal>() has BigDecimal as type argument.
When you call foo(BigDecimal.class), you're assuming that this was instantiated with BigDecimal as type argument, which is not always true, and the the compiler is preventing a bug.
The error in your code would be similar to a hypothetical bar() method in ArrayList<T> that does add("string"), which would be wrong for the same reason (the actual array list instance may be created to hold integers, not strings, so the inside code shouldn't make assumptions)
package org.my.java;
public class TestTypeVariable {
static <T,A extends T> void typeVarType(T t, A a){
System.out.println(a.getClass());
System.out.println(t.getClass());
}
public static void main(String[] s){
int i= 1;
typeVarType("string", i);
}
}
when run, following is the output :
class java.lang.Integer
class java.lang.String
How can A be of type Integer when it has been already upper-bounded to String?
Please explain me on it.
Two things here:
there is a simple solution to the "bad" typing: T isn't String but Object. And Integer extends Object. But please note: this only works with the "enhanced" type inference capabilities of Java8. With Java7, your input will not compile!
misconception on your end: getClass() happens at runtime, and therefore returns the specific class of the objects passed - independent on what the compiler thinks about generics at compile time.
How to overload a Function with generic parameter in Java 8?
public class Test<T> {
List<T> list = new ArrayList<>();
public int sum(Function<T, Integer> function) {
return list.stream().map(function).reduce(Integer::sum).get();
}
public double sum(Function<T, Double> function) {
return list.stream().map(function).reduce(Double::sum).get();
}
}
Error: java: name clash:
sum(java.util.function.Function<T,java.lang.Double>) and
sum(java.util.function.Function<T,java.lang.Integer>) have the same erasure
Benji Weber once wrote of a way to circumvent this. What you need to do is to define custom functional interfaces that extend the types for your parameters:
public class Test<T> {
List<T> list = new ArrayList<>();
#FunctionalInterface
public interface ToIntFunction extends Function<T, Integer>{}
public int sum(ToIntegerFunction function) {
return list.stream().map(function).reduce(Integer::sum).get();
}
#FunctionalInterface
public interface ToDoubleFunction extends Function<T, Double>{}
public double sum(ToDoubleFunction function) {
return list.stream().map(function).reduce(Double::sum).get();
}
}
Another way is to use java.util.function.ToIntFunction and java.util.function.ToDoubleFunction instead:
public class Test<T> {
List<T> list = new ArrayList<>();
#FunctionalInterface
public int sum(ToIntFunction function) {
return list.stream().mapToInt(function).sum();
}
public double sum(ToDoubleFunction function) {
return list.stream().mapToDouble(function).sum();
}
}
The example you present in your question has got nothing to do with Java 8 and everything to do with how generics work in Java. Function<T, Integer> function and Function<T, Double> function will go through type-erasure when compiled and will be transformed to Function. The rule of thumb for method overloading is to have different number, type or sequence of parameters. Since both your methods will transform to take a Function argument, the compiler complains about it.
That being said, srborlongan has already provided one way to resolve the issue. The problem with that solution is that you have to keep modifying your Test class for each and every type of operation (addition,subtraction,etc) on different types (Integer,Double, etc). An alternate solution would be to use method overriding instead of method overloading :
Change the Test class a bit as follows :
public abstract class Test<I,O extends Number> {
List<I> list = new ArrayList<>();
public O performOperation(Function<I,O> function) {
return list.stream().map(function).reduce((a,b)->operation(a,b)).get();
}
public void add(I i) {
list.add(i);
}
public abstract O operation(O a,O b);
}
Create a subclass of Test that will add two Integers.
public class MapStringToIntAddtionOperation extends Test<String,Integer> {
#Override
public Integer operation(Integer a,Integer b) {
return a+b;
}
}
Client code can then use the above code as follows :
public static void main(String []args) {
Test<String,Integer> test = new MapStringToIntAddtionOperation();
test.add("1");
test.add("2");
System.out.println(test.performOperation(Integer::parseInt));
}
The advantage of using this approach is that your Test class is in line with the open-closed principle. To add a new operation such as multiplication, all you have to do is add a new subclass of Test and override the operation method to multiply two numbers. Club this with the Decorator pattern and you can even minimize the number of sub-classes that you have to create.
Note The example in this answer is indicative. There are a lot of areas of improvement (such as make Test a functional interface instead of an abstract class) which are beyond the scope of the question.
#srborlongan 's solution won't work very well :)
See a similar example - Comparator methods - comparingDouble(ToDoubleFunction), comparingInt(ToIntFunction), etc. The methods have different names, because overloading is not a good idea here.
The reason is, when you do sum(t->{...}), the compiler is unable to infer which method to call; actually it needs to resolve method overloading first, to pick one method, before inferring the type of the implicit lambda expression (based on that method's signature)
This is disappointing. In the earlier stage, Java8 had a more sophisticated inference engine, and Comparator had overloaded comparing() methods; and sum(t->{...}) would be correctly inferred too. Unfortunately, they decided to simply it :( And here we are now.
Rule of thumb for overloading methods with functional arguments: the arities of the functional interfaces must be different, unless both are 0.
// OK, different arity
m1( X->Y )
m1( (X1, X2)->Y )
// not OK, both are arity 1
m2( X->Y )
m2( A->B )
m2( t->{...} ); // fail; type of `t` cannot be inferred
// OK! both are arity 0
m3( ()->Y )
m3( ()->B )
The reason why overloading with arity 0 is OK is that the lambda expressions won't be implicit - all argument types are known (because there's no argument!), we don't need contextual information for inferring the lambda type
m3( ()-> return new Y() ); // lambda type is ()->Y
m3( ()-> return new B() ); // lambda type is ()->B
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 Calculator<T> {
public Calculator(){
}
public T calculateSum(T a,T b){
return a; // I need to do something like a + b
}
}
I need to add the T object, I'm restricting the T to be only numeric types. How can I do this?
Thanks
Generic type arguments must be reference types, which excludes arithmetic types. You can give a boxed type such as Integer as the type argument, but that will not let you do arithmetic because auto-unboxing only works for particular, specific boxed types. Even if you restricted the type parameter to be a descendant of Number, there would be no way the compiler could be sure that the actual type argument was not some user-defined subclass of Number that did not have any auto-unboxing behavior.
More technically, generic types will not let you do anything that you wouldn't be able to achieve by inserting downcasts (and nothing else) in non-generic code. Here, it looks like you want to have a different kind of addition (integer or floating-point) depending on which type the argument is. And there is no way to achieve that solely by inserting casts; you'd have to insert some explicit condition that tested if the values happen to be Integer, then Long, then Float, et cetera. Since this is more complex than a downcast here or there, generics cannot, in principle, to this for you.
This is different from C++ templates, where each instantiation of the template generates (in principle, at least) a separate implementation in the compiler.
What you can do is make Calculator<T> an abstract class or an interface. Then make concrete subtypes such as
public class IntCalculator extends Calculator<Integer> {
public Integer calculateSum(Integer a, Integer b) {
return a+b;
}
}
public class DoubleCalculator extends Calculator<Double> {
public Double calculateSum(Double a, Double b) {
return a+b;
}
}
and so forth.
Methods (as well as classes) can have type parameters.
public class Calculator {
public Calculator(){
}
public <T extends Number > T calculateSum(T a,T b){
return a; // I need to do something like a + b
} }
Generics are not needed for this exercise.
public class Calculator {
public Calculator(){
}
public int calculateSum(int a,int b){
return a + b ;
}
public long calculateSum(long a,long b){
return a + b ;
}
.... you fill in the rest ...
}
In your declaration you need to make sure that T is always a sub-type of Number.
Instead of
Calculator<T>
try
Calculator<T extends Number>
then look in java.lang.Number and you should find a way to get a value that works with +.