4

I'm currently studying for Object-Oriented Programming and I'm currently studying the chapter Generics. I have the following code from a Java class and what I'm asked to do is to make the last method calculateSquares generic. I am really puzzled by it, I can tell you that.

Here is the class :

public class OefeningWildcards {

public static void main(String[] args){
    new OefeningWildcards().run();
}

public OefeningWildcards(){

}

public void run(){
    Number[] numbers = {2, 3.1, 5, 12, 2.3};
    List<Number> numbersList = new ArrayList<>();
    for (Number n : numbers){
        numbersList.add(n);


    List<Number> dest = new ArrayList<>();
    calculateSquares(dest, numbersList);

    Double[] doubles = {3.2, 5.6, 4.4, 6.5, 12.2};
    List<Double> doublesList = new ArrayList<>();

    List<Number> dest2 = new ArrayList<>();
    calculateSquares(dest2, doublesList);

    List<Double> dest3 = new ArrayList<>();
    calculateSquares(dest3, doublesList);

    }
}


//This method needs to become generic
public void calculateSquares(List<Number> dest, List<Number> src){
    dest.clear();
    for (Number n : src) {
        dest.add(n * n.intValue());
        dest.add(n.doubleValue() * n.doubleValue());
    }
}

What I found out is that List Number needs to become List ? extends Number, like this :

public void calculateSquares(List<? extends Number> dest, List<? extends Number> src){
    dest.clear();
    for (Number n : src) {
        dest.add(n * n.intValue());
        dest.add(n.doubleValue() * n.doubleValue());
    }
}

Thing is, I don't know what to do with the for-loop :

for (Number n : src) {
        dest.add(n * n.intValue());
        dest.add(n.doubleValue() * n.doubleValue());
    }
}

My logical guess would have been :

for (? extends Number n : src) {
        dest.add(n * n.intValue());
        dest.add(n.doubleValue() * n.doubleValue());
    }
}

But that seems to be incorrect. What should I do to the for-loop?

Tom
  • 14,120
  • 16
  • 41
  • 47
  • At line 3 : **dest.add(n * n.intValue());** is a "bad operand types for binary operator '*'" error. At line 4 : **dest.add(n.doubleValue() * n.doubleValue());** I get a "No suitable method for add(double)" error. –  Dec 11 '15 at 14:05
  • In the case of the second compiler error, you can't add to a `List extends T>` because you don't know what `? extends T` is. Say `T` is `Number`: `? extends Number` might be `Number`, it might be `Integer`, or it might be some class you've just made up which implements `Number`. You can, however, add to `List super T>`. (See http://stackoverflow.com/questions/2723397/java-generics-what-is-pecs) – Andy Turner Dec 11 '15 at 14:18
  • In the case of the first compiler error, that's because `*` only applies to primitive types, or types which can be auto-unboxed. `Number` can't be. You'd need to call, say, `n.intValue() * n.intValue()` or `n.doubleValue() * n.intValue()`. – Andy Turner Dec 11 '15 at 14:23
  • 1
    Don't write "solved" into your question title. Accepting the correct answer (the one which answered your question) is all you need to do. – Tom Sep 02 '16 at 12:56

2 Answers2

2

What you actually ask for is not possible. At least you can't change the destination list to ? extends Number. Because then you don't know which type the list is. If it is a List<Integer> you can't add a Double value there. However what you can do is make the source generic.

The way to go is to add the generic type to the method declaration

public void <T extends Number> calculateSquares(List<Number> dest, List<T> src){
    dest.clear();
    for (T n : src) {
        dest.add(n * n.intValue());
        dest.add(n.doubleValue() * n.doubleValue());
    }
}

actually once you leave the destination you also can leave your old Method. It still works:

public void calculateSquares(List<Number> dest, List<? extends Number> src){
    dest.clear();
    for (Number n : src) {
        dest.add(n * n.intValue());
        dest.add(n.doubleValue() * n.doubleValue());
    }
}

as it seems you want add a List<Double> or a List<Number> to destination. This is possible again by altering the first List and use the super-operator:

public void calculateSquares(List<? super Double> dest, List<? extends Number> src){
    dest.clear();
    for (Number n : src) {
        dest.add((double)n * n.intValue());
        dest.add(n.doubleValue() * n.doubleValue());
    }
}
Denis Lukenich
  • 2,804
  • 1
  • 16
  • 38
0

First, there is no need to pass the dest List. Altering values of parameters is not a good practice. We can probably change the return type to List, create a new List ourselves and return it.

Second, means that you can pass any list that extends number but you can't add anything into it. If you want to add then you need to change it to . Refer to this SO answer for in depth explanation.

Third, we don't need to add the square twice. Adding just once should do. Refer to below example:

public List<Number> calculateSquares(List<? extends Number> src){
        List<Number> squares = new ArrayList<>();
        for (Number n : src) {
            squares.add(n.doubleValue() * n.doubleValue());
        }
        return squares;
    }
Community
  • 1
  • 1
Darshan Mehta
  • 27,835
  • 7
  • 53
  • 81