2

I have a List of generic objects of type MyClass<T> of which I do not know the exact type: List<MyClass<? extends Object>>.

Is there a way to filter this list to obtain only those objects that have a specific T without casting or using instanceof?.

I have the feeling this can be done with the Visitor pattern and subclassing per specific instance of T (like MyClassString extends MyClass<String>, which is not a problem), but I do not want to implement a Visitor for every subclass:

class MyClassString extends MyClass<String> {

    public <U> U accept(MyClassVisitor<U> visitor) {
        return visitor.visitMyClassString(this);
    }

}

interface MyClassVisitor<U> {

    U visitMyClassString(MyClassString myClassString);
    U visitMyClassDouble(MyClassDouble myClassDouble);
    ...

}

class FilterMyClassStringService implements MyClassVisitor<List<MyClassString>> {

    public List<MyClassString> filterMyClassString(List<MyClass<? extends Object>> toFilter) {
        List<MyClassString> filteredList = new ArrayList<>();
        for (MyClass<? extends Object> elem : toFilter) {
            filteredList.addAll(elem.accept(this));
        }
        return filteredList;
    }

    @Override
    public List<MyClassString> visitMyClassString(MyClassString myClassString) {
        List<MyClassString> listWithMyClassString = new ArrayList<>();
        listWithMyClassString.add(myClassString);
        return listWithMyClassString;
    }

    @Override
    public List<MyClassString> visitMyClassDouble(MyClassDouble myClassDouble) {
        return new ArrayList<MyClassString>();
    }

}

Is there any way?

Werner de Groot
  • 623
  • 4
  • 14
  • If you are using Java 8, you can use a stream to filter. – Tim Biegeleisen Aug 27 '15 at 07:06
  • 1
    could you tel us why you dont want to use instance Of is it expensive? – Nassim MOUALEK Aug 27 '15 at 08:06
  • Honestly, I just want to know if it can be done. One reason against using `instanceOf` in many places is that it is easy to forget adding another case to each `if` that checks the type. The Visitor pattern forces everyone who switches on the type (using a visitor) to consider a new type, when it is added to the program. – Werner de Groot Aug 27 '15 at 08:45

1 Answers1

1

I think I understand what you would like to have. And it's possible in some way. If we apply the discussion from this thread (How to get the generic type at runtime?) to your case then we'll have something like this code:

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

public class TypedReflectTest {
    public static void main(String[] args) {
        List<MyClass<?>> list = new ArrayList<MyClass<?>>();
        list.add(new MyClassInteger(123));
        list.add(new MyClassDouble(123.45));
        list.add(new MyClassString("1234"));
        System.out.println(filterMyClass(list, Integer.class));
    }

    public static List<MyClass<?>> filterMyClass(List<MyClass<?>> toFilter, Class<?> innerType) {
        List<MyClass<?>> filteredList = new ArrayList<MyClass<?>>();
        for (MyClass<?> elem : toFilter) {
            if (!elem.getInnerType().equals(innerType))
                continue;
            filteredList.add(elem);
        }
        return filteredList;
    }
}

abstract class MyClass<T> {
    private T value;
    private Class<?> innerType;

    public MyClass(T value) {
        this.value = value;
        Type superClass = getClass().getGenericSuperclass();
        innerType = (Class<?>)(((ParameterizedType) superClass).getActualTypeArguments()[0]);
    }

    public T getValue() {
        return value;
    }

    public Class<?> getInnerType() {
        return innerType;
    }

    @Override
    public String toString() {
        return "MyClass(" + value + ")";
    }
}

class MyClassInteger extends MyClass<Integer> {
    public MyClassInteger(Integer value) {
        super(value);
    }
}

class MyClassDouble extends MyClass<Double> {
    public MyClassDouble(Double value) {
        super(value);
    }
}

class MyClassString extends MyClass<String> {
    public MyClassString(String value) {
        super(value);
    }
}

You should notice that abstract super class is essential here. You can not deal with instances of MyClass type itself. Unfortunately you have to extend this class in order to get parameterized type in runtime.

Community
  • 1
  • 1
rsutormin
  • 1,389
  • 2
  • 13
  • 20