Collection homogenization

Suppose I have a non-generic List or List<Foo> , for which you know that all elements are of type Bar , which is a subclass of Foo (which will always be the case). Suppose you cannot change the code that gets the original list so that you can directly get the List<Bar> . Is there any library or some other thing that helps to “homogenize” this list?

That is, is there a function in Vienna

 public static <T, U extends T> List<U> homogenize(List<T> list, Class<U> subclass); 

what can i use somewhere? ( homogenize() returns an input list representation with functions that take U , not T or Object where necessary. It does not return a copy of the list. If the input list cannot be homogenized, that is, if not all elements are of type, assigned by U then homogenize() returns null.)

+4
source share
2 answers

If you are looking for a library, my first thought was to use Google Guava Libraries as follows:

 public <T, U extends T> List<U> homogenize(List<T> list, final Class<U> subclass) { Predicate<T> pred = new Predicate<T>() { @Override public boolean apply(T input) { return input.getClass().isAssignableFrom(subclass); } }; return Iterables.all(list, pred) ? (List<U>)list : null; } 

I have not tried to make sure that there are no kinks. However, I looked at him and decided that he was pretty butt-ugly. Guwa's approach is slightly better:

 public <T, U extends T> List<U> homogenize(List<T> list, Class<U> subclass) { Iterable<U> ret = Iterables.filter(list, subclass); if (list.size() != Lists.newArrayList(ret).size()) return null; return (List<U>)list; } 

However, it is still a little ugly. And he uses an internal copy of the collection. It still returns the cast look of the original. After all, everything has been said and done, the cleanest approach seems to use ordinary Java:

 public <T, U extends T> List<U> homogenize(List<T> list, Class<U> subclass) { for( T t : list) { if (!t.getClass().isAssignableFrom(subclass)) return null; } return (List<U>)list; } 

Depending on your aversion to style warnings, you can even opt out of broadcast operators in all three options.

EDIT ON COMMENTS The following changes / improvements were proposed in the comments.

Option one is improved:

 public <T, U extends T> List<U> homogenize(List<T> list, final Class<U> subclass) { return Iterables.all(list, Predicates.instanceOf(subclass)) ? (List<U>)list : null; } 

Improved second option:

 public <T, U extends T> List<U> homogenize(List<T> list, Class<U> subclass) { Iterable<U> ret = Iterables.filter(list, subclass); return (list.size() != Iterables.size(ret)) ? null : (List<U>)list; } 

Option three is improved:

 public <T, U extends T> List<U> homogenize(List<T> list, Class<U> subclass) { for( T t : list) { if (!subclass.isInstance(t.getClass())) return null; } return (List<U>)list; } 

With these improvements, the first Guava example shines quite a bit. If you don't mind static imports, both Guava examples become extremely readable.

+4
source

Why can't you just drop it?

 public static <U> List<U> homogenize(List<? super U> list){ return (List<U>)list; } 

Note that generics is purely compile-time checking. If the types do not match what you expect, you will get an exception if you try to apply the bar object to bar .

Edit: I didn’t notice that you want it to really check the content type and return null on error. If you do not know for sure that the elements are of the correct type and want to work with an error, you will be better off with one of the other approaches.

0
source

All Articles