I am currently learning about Generics, first I made the Class which makes a stackArrayList with Integers, after that I wanted it to be used by every Number so I did put the Number in everywhere in the Methods, it kind of worked but I think this is not quiet correct, my biggest Problem is that I cant use any addition or subtraction with the Class Number.
So I tried implement the N extends Number but that does not work either and with that I can’t even put a Number which returns from the Method pop() inside of a variable, telling me that pop() must return an int then.
Can someone explain to me please, what I did there? I can grasp the logic behind of it but cannot understand it to the fullest.
public class StackArrayList<N extends Number> { private ArrayList<N> stackList = new ArrayList<>(); public void push (N value) { if(value == null) { System.out.println("value null not allowed"); return; } stackList.add(0,value); } public N pop() { N popInt = stackList.get(0); stackList.remove(0); return popInt; } public N addFirstTwo() { if(stackList.size()==1) { return stackList.get(0); } if (stackList.size() == 0) {return null;} if(stackList.get(0) instanceof Integer) { N summe; summe = push(summe); return summe; }else if(stackList.get(0) instanceof Double){ summe = pop().doubleValue()+pop().doubleValue(); push(summe); return summe; }else if(stackList.get(0) instanceof Float){ summe = pop().floatValue()+pop().floatValue(); push(summe); return summe; }else if(stackList.get(0) instanceof Long){ summe = pop().longValue()+pop().longValue(); push(summe); return summe; } push(summe); return summe; } }
Advertisement
Answer
The problem is that Number
does not support the +
operator, even though all its implementations (Double
, Float
etc.) do support it.
What you can do is to define an inner interface to your class which exposes a sum
method:
interface SumPerformer<N> { N sum(N n1, N n2); }
Then, you will declare a field into your StackArrayList<N extends Number>
class that holds an implementation of this interface:
private final SumPerformer<N> sumPerformer;
And so, you will use this implementation to return the sum of the two first numbers inside your addFirstTwo()
method:
public N addFirstTwo() { if (stackList.isEmpty()) { return null; } if (stackList.size() == 1) { return stackList.get(0); } return sumPerformer.sum(stackList.get(0), stackList.get(1)); }
When it’s time to initialize your StackArrayList
with a concrete implementation of Number
, you will simply pass it the implementation’s sum
by method reference:
StackArrayList<Double> stackArrayListDoubles = new StackArrayList<>(Double::sum); StackArrayList<Long> stackArrayListLongs = new StackArrayList<>(Long::sum); StackArrayList<Integer> stackArrayListInts = new StackArrayList<>(Integer::sum);
Full code:
public static final class StackArrayList<N extends Number> { private final List<N> stackList = new ArrayList<>(); private final SumPerformer<N> sumPerformer; public StackArrayList(SumPerformer<N> sumPerformer) { this.sumPerformer = sumPerformer; } public N pop() { N popInt = stackList.get(0); stackList.remove(0); return popInt; } public void push(N value) { if (value == null) { System.out.println("value null not allowed"); return; } stackList.add(0, value); } public N addFirstTwo() { if (stackList.isEmpty()) { return null; } if (stackList.size() == 1) { return stackList.get(0); } N sum = sumPerformer.sum(stackList.get(0), stackList.get(1)); push(sum); return sum; } interface SumPerformer<N> { N sum(N n1, N n2); } }
Answers to the questions raised in the comments
Doesn’t the Variable need to be inside the <> ?
No. The <>
(called diamond operator) is used to infer a specific type to a class which takes a “generic” type (you can read more about generics).
Before Java 8, you needed to specify the type both in the declaration and the instantiation of the variable. For example, the interface List<T>
is implemented by the class ArrayList<T>
.
Before Java 8, if you wanted a list of Integer
you needed to do:
List<Integer> list = new ArrayList<Integer>();
After Java 8, you can simply state:
List<Integer> list = new ArrayList<>();
Indeed, the type Integer
will be inferred to ArrayList
by the declaration List<Integer>
.
This is why, when I initialize my StackArrayList<>
, I’m not passing Double
to it because I already pass it to the declaration, so the variable already knows what is its type.
shouldnt it just pass down the Number i initialize the Object with ?
Careful, you’re mixing up stuff.
The reason why you shouldn’t pass Double
is explained above.
However, here I am doing something else.
If you check the new constructor of StackArrayList
, I’ve added a member which is private final SumPerformer<N> sumPerformer;
.
This member is final
, it means it should be initialized when you initialize the class, and I do that by passing it into the constructor.
But so what am I passing to the constructor? I am passing a concrete implementation of the abstract interface that I define.
Specifically, I have declared the interface SumPerformer<N>
to expose only one method which is called sum
, returns a N
and takes in input two distinct N
.
A concrete implementation of this interface, hence, should be a method with the same signature.
And this method, is the sum()
method of all Number
children (Double
, Integer
…)
If you go into the class Double
, you will see that there is this method:
public static double sum(double a, double b) { return a + b; }
The above method’s signature matches with your interface when the type N extends Number
is Double
.
So, in your class, you simply call the interface .sum()
to sum up your two numbers, and you leave the implementation details (the a + b
, basically) to the concrete implementation which is not done by you, but by Java itself in the JDK’s classes that extend Number
.