Using values in a generic list with wild cards

I am trying to make an abstract class that has a list of Objects of type T, that also has a function that takes a String value and a single Object of the same type T:

public abstract class MyAbstractClass<T> {
    private String title;
    private List<T> dataRows;

    public String getTitle() {
        return this.title;
    };
    public void setTitle(String title) {
        this.title = title;
    }

    public List<T> getDataRows() {
        return dataRows;
    }

    public void setDataRows(List<T> dataRows) {
        this.dataRows = dataRows;
    }

    public abstract String getValueFromRow(String key, T dataRow);
}

Which I extend like so (using MyObj1, MyObj2, etc…):

public class MyClass extends MyAbstractClass<MyObj> {

    public String getValueFromRow(String key, MyObj dataRow) {
        //Do stuff
    }
}

I want to process different implementations of the getValueFromRow() method like so:

public void doWork(MyAbstractClass<?> obj) {

    for (int i = 0; i < obj.getDataRows.size(); i++) {
        for (int j = 0; j < myKeys.size(); j++) {
            // This line does not work. I get an error saying this is an invalid type
            // even though getDataRows() is List<T> and the method requires type T
            String val = obj.getValueFromRow(myKeys.get(j), obj.getDataRows().get(i));
            doStuff(val);
        }
    }
}

But when I try to use a value from the List<T> of objects in the method that requires an Object of type T I get the error: Wrong Second Argument Type. Found '?', required '?'. Where am I going wrong?

I have tried replacing the <?> with <Object> but then I get an error when trying to call the method:

MyClass obj = new MyClass();
obj.setDataRows(data);
obj.setTitle("My Title");

//This is now the wrong input type when using <Object> rather than <?> in the service
myService.doWork(obj);

Answer

When you use ?, you’re telling the compiler that you don’t care about the type and it doesn’t matter. But the object cares and it matters as it has to ensure that the argument has the right type. Here is an analogous example of your problem:

import java.util.ArrayList;
import java.util.List;

class Main {
    
    static class C<T> {
        List<T> l = new ArrayList<>();
        
        T get(int i) { return l.get(i); }
        void add(T t) { l.add(t); }
    }
    
    static void doThings(C<?> c) {
        c.add(c.get(0)); // 1
        
        // even clearer
        String s = c.get(0); // 2
        c.add(s);
        
        // doesn't matter
        Object o = c.get(0);
        c.add(o); // still an error
    }
    
    
    public static void main(String[] args) {
        C<String> c = new C<>();
        c.add("hello");
        doThings(c);
    }
}

At *1 is basically shown what you’re doing. At *2 the problem gets clearer. You tell the compiler to not care about the type but it has to at *2 (and *1).

To solve this, you can make the method generic just like the class:

    static <T> void doThings(C<T> c) {
        c.add(c.get(0));
        
        // works as T is known at compile-time
        T s = c.get(0);
        c.add(s);
    }

Leave a Reply

Your email address will not be published. Required fields are marked *