Java return ArrayList by type

Viewed 78

I have a simple function that returns a specific ArrayList but I'm wondering if there is a way to return one by its type?

I have this:

ArrayList<Tile> getTiles() {
    ArrayList<Tile> tiles = new ArrayList<>();
    for (GameObj obj : gameObjs) {
        if (obj instanceof Tile)
            tiles.add((Tile) obj);
    }
    return tiles;
}

and this:

ArrayList<Obj> getObjs() {
    ArrayList<Obj> objs = new ArrayList<>();
    for (GameObj obj : gameObjs) {
        if (obj instanceof Obj)
            objs.add((Obj) obj);
    }
    return objs;
}

So I'll end up with multiple functions just to return a simple ArrayList again and again. Of course Tile, Obj etc extend GameObj. I'm wondering if there's a way to return whichever type I require. I tried this, but it doesn't work:

ArrayList<GameObj> getByType(Class<? extends GameObj> whichType) {
    ArrayList<GameObj> objs = new ArrayList<>();
    for (GameObj obj : gameObjs) {
        if (obj.getClass() == whichType)
            objs.add(obj);
    }
    return objs;
}

ArrayList<Tile> tiles = (ArrayList<Tile>) getByType(Tile.class);

I get the error Cannot cast from ArrayList<GameObj> to ArrayList<Tile>

1 Answers

I commented the part that you need, comment in/out the other respective line, you'll see the difference in the results.

package stackoverflow;

import java.util.ArrayList;

public class ReturnListByType {

    public interface MyInterface { /* */ }
    static public class MyImpl1 implements MyInterface {
        private final String mName;
        public MyImpl1(final String pName) {
            mName = pName;
        }
        @Override public String toString() {
            return "MyImpl1: " + mName;
        }
    }
    static public class MyImpl2 implements MyInterface {
        private final int mValue;
        public MyImpl2(final int pValue) {
            mValue = pValue;
        }
        @Override public String toString() {
            return "MyImpl2: " + mValue;
        }
    }


    public static void main(final String[] args) {
        {
            System.out.println("List 1:\n");
            final ArrayList<MyInterface> mixedList = new ArrayList<>();
            mixedList.add(new MyImpl1("Hello!"));
            mixedList.add(new MyImpl2(667));
            mixedList.add(new MyImpl2(12345));
            mixedList.add(new MyImpl1("World"));
            {
                System.out.println("Strings: (will show no results)");
                final ArrayList<String> list = filterList(mixedList, String.class);
                for (final String i : list) {
                    System.out.println("\t" + i);
                }
            }
            {
                System.out.println("Integers: (will show no results");
                final ArrayList<Integer> list = filterList(mixedList, Integer.class);
                for (final Integer i : list) {
                    System.out.println("\t" + i);
                }
            }
            {
                System.out.println("MyImpl1:");
                final ArrayList<MyImpl1> list = filterList(mixedList, MyImpl1.class);
                for (final MyImpl1 i : list) {
                    System.out.println("\t" + i);
                }
            }
            {
                System.out.println("MyInterface:");
                final ArrayList<MyInterface> list = filterList(mixedList, MyInterface.class);
                for (final MyInterface i : list) {
                    System.out.println("\t" + i);
                }
            }
        }
        {
            System.out.println("\n\nList 2:\n");
            final ArrayList<MyImpl1> mixedList2 = new ArrayList<>();
            mixedList2.add(new MyImpl1("Yes"));
            mixedList2.add(new MyImpl1("Sir!"));
            {
                System.out.println("MyInterface with List2:");
                final ArrayList<MyInterface> list = filterList(mixedList2, MyInterface.class);
                for (final MyInterface i : list) {
                    System.out.println("\t" + i);
                }
            }
            {
                System.out.println("MyInterface with List2, MyImpl1:");
                final ArrayList<MyImpl1> list = filterList(mixedList2, MyImpl1.class);
                for (final MyImpl1 i : list) {
                    System.out.println("\t" + i);
                }
            }
            {
                System.out.println("MyInterface with List2, MyImpl2, cannot show any values:");
                final ArrayList<MyImpl2> list = filterList(mixedList2, MyImpl2.class);
                for (final MyImpl2 i : list) {
                    System.out.println("\t" + i);
                }
            }
            {
                System.out.println("MyInterface with List2, String, cannot show any results:");
                final ArrayList<String> list = filterList(mixedList2, String.class);
                for (final String i : list) {
                    System.out.println("\t" + i);
                }
            }
        }
    }

    static public <T, U extends MyInterface> ArrayList<T> filterList(final Iterable<? extends U> pItems, final Class<? extends T> pClass) {
        final ArrayList<T> ret = new ArrayList<>();
        for (final Object o : pItems) {
            // use any1 of the following 3 lines
            //          if (o.getClass().isAssignableFrom(pClass)) ret.add(pClass.cast(o)); // either use this line, direct and parents match
            //          if (pClass.isAssignableFrom(o.getClass())) ret.add(pClass.cast(o)); // or this line, direct and child match
            if (pClass.isInstance(o)) ret.add(pClass.cast(o)); // or this line, direct and child match
        }
        return ret;
    }



}

Update

You could also limit the type parameter T to something like static public <T extends GameObj> ArrayList<T> filterList(final Iterable<Object> pItems, final Class<T> pClass) { if you need that.

Update 2

Completely reworked the code according to @robspoor 's answer.
And it now takes care of you type bound list problem. This now works to your specifications.
As for your Java Version, I think it support streams, but I rather leave it the old-fashioned iterative way in case you wanna do some easy edits.

Note: This solution keeps the type parameters T and U separated (T unconstrained) so you have more freedom in throwing in typed lists. You could connect T and U or simply have only 1 (it would be U) if that's more comfortable for you to use.

Related