Can I make this java pluck() method more safe? - java

I wrote this utility function:
public static <T> List<T> pluck(String fieldName, List list)
throws NoSuchFieldException, IllegalAccessException {
if (list.isEmpty()) {
return new ArrayList<T>();
}
Class c = list.get(0).getClass();
Field f = c.getField(fieldName);
ArrayList<T> result = Lists.newArrayList();
for (Object object : list) {
result.add((T) f.get(object));
}
return result;
}
I copied the idea from underscore.js. The use case is:
ArrayList<Person> people = new ArrayList<Person>;
people.add(new Person("Alice", "Applebee"));
people.add(new Person("Bob", "Bedmington"));
people.add(new Person("Charlie", "Chang"));
List<String> firstNames = pluck("firstName", people);
My problem is that if the caller gets the type wrong, no exception is thrown until the caller tried to get an object from the list. Ideally, I'd like to throw a ClassCastException from the pluck method itself. However, I don't see a way to access the type of the list on run time.
Is there some trick I can use to make sure the caller doesn't end up with an invalid list?
Edit: So using the feedback I got, a safe implementation would be:
public static <T,F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType)
throws NoSuchFieldException, IllegalAccessException {
Field f = listType.getField(fieldName);
ArrayList<F> result = new ArrayList<F>();
for (T element : list) {
result.add(fieldType.cast(f.get(element)));
}
return result;
}
But actually lambdaj seems to do what I wanted, so I guess I'll use that. Thanks mike!
Disclaimer: LambdaJ ( #GoogleCode | #GitHub ) - This project is not maintained anymore since the release of JDK8 (JSR 335, JEP 126).

You can change your signature to as follows:
public static <T, F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType)
The you have the list type and field type.

Why dont you define the signature like this:
public static <T, U> List<T> pluck(String fieldName, Class<T> fieldType, List<U> list);
This would:
1) Force the client to supply the type of the field he wants to "pluck", so you can do proper type-checking in your method.
2) Force the client to supply a generic list from which to "pluck", so you prevent another error-source (the client supplying a list that contains objects of different types).
I think this is as safe as it can get..

What do you mean by invalid list? If you mean that they try to cast it to something it is not then try changing the declaration to public static <T> List<T> pluck(String fieldName, List<T> list).
I'm confused by the However, I don't see a way to access the type of the list on run time. comment. However, if I understand you correctly then: there is no "type" at runtime because generics in Java are implemented by "erasure". This means that the compiler checks at compile time that it works, and then turns it into regular casts like we had before generics. This was necessary they felt to enable backward and forward compatibility.

You should use generics for the type parameter, and pass in the class object of the return type:
public static <TItem, TResult> List<TResult> pluck(String fieldName, List<TItem> list, Class<TResult> resultType)
throws NoSuchFieldException, IllegalAccessException {
if(list.isEmpty()) return new ArrayList<TResult>();
Class c = list.get(0).getClass();
Field f = c.getField(fieldName);
ArrayList<TResult> result = new ArrayList<TResult>();
for(Object object : list) {
result.add(resultType.cast(f.get(object)));
}
return result;
}
Generally, when you get a warning about an unsafe cast to a type parameter, you should see if you can replace it with a call to Class.cast

you can try the one given by Google collections library instead of maintaining a new one: Collections2.transform like this
Collection<Y> yourCollection...
...
Collection<X> expected = Collections2.transform(yourCollection, new Function<Y, X>() {
public X apply(Y element) {
return element.getX();
}
}

With Google's Guava collections library, you can use Collections2.transform().
Usage
Given an interface/class, e.g. called Entity, your class can implement/extend this.
public abstract class Entity {
private long id;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
}
public interface Entity {
long getId();
}
Now you can Retrieve a list of each Entity's IDs.
import com.google.common.base.Function;
import com.google.common.collect.Collections2;
public class Main {
public static void main(String[] args) {
List<Entity> entities = ...
List<Long> ids = pluckIds(entities);
}
public static <E extends Entity> List<Long> pluckIds(List<E> list) {
return new ArrayList<Long>(Collections2.transform(list, new Function<E, Long>() {
public Long apply(E entity) {
return entity.getId();
}
});
}
}
This is the safest you can get. This satisfies proper OOP principles and Java 5-7.
In Java 8, you can achieve the same effect with a stream, map, and lambda
public static <E extends Entity> List<Long> pluckIds(List<E> list) {
return list.stream().map(e -> e.getId()).collect(Collectors.toList());
}
or
public static <T,F> List<F> pluck(String fieldName, Class<F> fieldType,
List<T> list, Class<T> listType) throws NoSuchFieldException,
IllegalAccessException, IllegalArgumentException {
Field f = listType.getDeclaredField(fieldName);
f.setAccessible(true);
return list.stream().map(e -> {
try { return fieldType.cast(f.get(e)); } catch (Exception e1) { return null; }
}).collect(Collectors.toList());
}

Not sure what you are asking, but you could try:
Class c = list.get(0).getClass();
if (!c.equals(Person.class))
throw new ClassCastException();

You can cast the list to a java.lang.reflect.ParameterizedType and checking that the array returned by getActualTypeArguments() contains the class you need. Other than that, you're out of luck.

Related

Java Generics: getting return type from parameter

This is an odd question. I don't think there's a solution, but I thought I'd ask anyway.
Say I have an enum:
public enum Key {
RED(String.class),
GREEN(Integer.class),
BLUE(Short.class);
private Class<?> expectedType;
Key(Class<?> expectedType) { this.expectedType = expectedType; }
public Class<?> getExpectedType() { return expectedType; }
}
I want to use the 'expectedType' field from the Key enum as the return type of a method. See:
public class Cache {
private static Map<Key, Object> cache = new HashMap<>();
public void put(Key key, Object value) {
// Easy to validate that 'value' is of type key.getExpectedType()...
}
public <T> T get(Key key) {
Object value = cache.get(key);
// TODO need to define <T> as key.getExpectedType(). How?
}
}
See that TODO? I'd like for get() to define the return type of the 'expectedType' defined by the key parameter. E.g. if the key parameter were RED, the get() method would return a String and you could write:
String s = cache.get(Key.RED);
Is there a way to do that?
I'm thinking there isn't, but I'd love to hear of a clever solution.
Enums don't support generics, but you could use a regular class as a generic pseudo-enum:
public class Key<T> {
public static final Key<String> RED = new Key<>(String.class);
public static final Key<Integer> GREEN = new Key<>(Integer.class);
public static final Key<Short> BLUE = new Key<>(Short.class);
private final Class<T> expectedType;
private Key(Class<T> expectedType) { this.expectedType = expectedType; }
public Class<T> getExpectedType() { return expectedType; }
}
public class Cache {
private Map<Key<?>, Object> cache = new HashMap<>();
public <T> void put(Key<T> key, T value) {
cache.put(key, key.getExpectedType().cast(value));
}
public <T> T get(Key<T> key) {
return key.getExpectedType().cast(cache.get(key));
}
}
shmosel's answer is almost certainly sufficient for what you need; however, it has the slight limitation that you can't store/retrieve a generic type, because you can't get a class literal for a generic type.
Instead, you can use something like Guava's TypeCapture:
abstract class GenericKey<T> {
Type getExpectedType() {
return ((ParameterizedType) getClass().getGenericSuperclass())
.getActualTypeArguments()[0];
}
}
which is a bit of reflective grossness that you shouldn't spend too much time looking at.
Notice that it's abstract, so you have to instantiate like:
new GenericKey<Integer>() {}
This is creating an anonymous subclass of GenericKey, which is part of the magic that makes it work with generic types.
Then, it's basically the same:
public class Cache {
private Map<GenericKey<?>, Object> cache = new HashMap<>();
public <T> void put(GenericKey<T> key, T value) {
cache.put(key.getExpectedType(), value);
}
public <T> T get(GenericKey<T> key) {
return (T) cache.get(key.getExpectedType());
}
}
Now you could have a GenericKey<List<Integer>>, using new new GenericKey<List<Integer>() {}, if you should so desire.
The downside of this approach is that you lose the ability to do checking on the value on the way in/out of the cache, so you could get heap pollution if you are careless with raw types.

Java Map with variable generics as values

So here's a slightly tricky question (for me).
I have a generic object. Call it MyObject. This object has a method which returns something of the type T:
public class MyObject<T>
{
private T _t;
public MyObject(T t)
{
_t = t;
}
//...
public T get()
{
return _t;
}
}
(Obviously my "MyObject" does a bit more but that's the gist).
Now, I want to have a map of this type:
Map<String, MyObject<?>> m = new HashMap<>();
I want to be able to fetch maps using some predefined string name, and these maps can be of any MyObject. For example, I could call:
m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));
etc.
But - and here's the tricky part - I want the map to "remember" the parameterized type of MyObject when I fetch some value from the map. Using
m.get("map_1");
would return a
MyObject<Object>
type, since the map was defined as containing
MyObject<?>
values. Thus:
m.get("map_1").get() // <-- This is an Object, not a String!
What modification (if any) is possible, in order to be able to get the correct - full - information regarding the MyObject fetched object, such that invoking the last line (m.get("map_1")) would return a
MyObject<String>
Thanks :)
Amir.
Typesafe Heterogeneous Containers from Joshua Bloch's Effective Java might work here. Basically you add a Class object to represent the type.
public class MyObject<T>
{
private T _t;
private Class<T> type;
public MyObject( Class<T> type, T t)
{
_t = t;
this.type = type;
}
//...
public T get()
{
return _t;
}
public Class<T> getType() { return type; }
}
Then you could do something like this:
public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
return type.cast( m.get( key ).get() );
}
Which is safe and will compile, but will throw a runtime error if you get the type wrong.
(Note I didn't actually compile that, so I might have syntax errors floating around. But most folks don't know how to use Class to cast objects.)
You can get the class.
Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
Here is a full test.
public class GenericsTest {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Map<String, MyObject<?>> map = new HashMap<>();
MyObject<String> obj = new MyObject<>("hello");
map.put("greeting", obj);
Class c = map.get("greeting").get().getClass();
if (String.class.equals(c)) {
System.out.println("its a String");
}
}
static class MyObject<T> {
T t;
public MyObject(T t) {
this.t = t;
}
T get() {
return t;
}
}
}
The type system only knows about types, not objects, and therefore can not distinguish "key1" from "key2", because both are of type String.
If keys have different types, the easiest way is to encapsulate a weakly typed map, and use reflective casts to prove to the compiler the types are correct:
class Favorites {
private Map<Class<?>,?> map = new HashMap<>();
<V> V get(Class<V> clazz) {
return clazz.cast(map.get(clazz));
}
<V> void put(Class<V> clazz, V value) {
map.put(clazz, value);
}
}
Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1);

java how to extract some property from a List of some object and also friendly to refactor

When develop java code, always need extract some property from a list of some object, e.g.
List<Foo> fooList = ...
List<Integer> idList = new ArrayList<>();
for(Foo f : fooList){
idList.add(f.getId());
}
because in production environment we used java7, so I cannot use java8 stream to implement this. So I write an util code to implement this
public static <T, O> List<T> extract(Collection<O> collection, String propertyName) {
List<T> result = new ArrayList<>(collection.size());
for (O o : collection) {
Object val = getFieldValue(propertyName, o);
result.add((T) val);
}
return result;
}
then I implement this just like below
List<Integer> idList = extract(fooList,"id");
but it's not friendly to refactor, if I changed the property name,e.g. id --> fooId, it cannot perceive to this change.
So I want to know how to implement this function refactor friendly and also use it easily?
Even if you don't use Java 8, you can apply the same logic by using a function as next:
public interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* #param t the function argument
* #return the function result
*/
R apply(T t);
}
public static <T, O> List<T> extract(Collection<O> collection, Function<O, T> mapper) {
List<T> result = new ArrayList<>(collection.size());
for (O o : collection) {
result.add(mapper.apply(o));
}
return result;
}
Indeed using a function instead of a String literal is much easier to refactor and much more Object Oriented.
Your method call will then be something like that:
List<Integer> idList = extract(fooList, new Function<Foo, Integer>() {
#Override
public Integer apply(final Foo f) {
return f.getId();
}
});
I think you can have a look to Guava. Guava has a Function interface and
Collections2.transform(Collection<E>, Function<E,E2>) method provides the feature you require. Following is an example:
final Collection<Foo> fooList = ...;
final Collection<Integer> idList =
Collections2.transform(fooList, new Function<Foo, Integer>(){
#Override
public Integer apply(final Foo foo){
return foo.getId();
}
});

How can I filter a list in Java using a predicate function?

Consider the following code:
ICondition searchCondition, scopeCondition...
List<ICondition> filtered = CollectionUtil.filter(
Arrays.asList(searchCondition, scopeCondition),
CollectionUtil.isNonNull);
It fails to compile:
"The method filter(Collection<T>, CollectionUtil.Predicate<T>) in the type CollectionUtil is not applicable for the arguments (List<ICondition>, CollectionUtil.Predicate<Object>)"
Everything is fine if I define an ICondition-specific isNonNull() predicate, but that's dumb and I don't understand what's wrong or how to fix it.
Here are my utility functions:
public interface Predicate<T>
{
boolean apply(T type);
}
public static <T> List<T> filter(Collection<T> target, Predicate<T> predicate)
{
target = Collections.unmodifiableCollection(target);
List<T> result = new ArrayList<T>();
for (T element: target) {
if (predicate.apply(element)) {
result.add(element);
}
}
return result;
}
// This predicate works as expected.
public static CollectionUtil.Predicate<String> isStringNonBlank = new CollectionUtil.Predicate<String>() {
public boolean apply (String item) {
return !StringUtils.isBlank(item);
}
};
// This predicate looks fine, but fails in usage.
public static CollectionUtil.Predicate<Object> isNonNull = new CollectionUtil.Predicate<Object>() {
public boolean apply (Object item) {
return null != item;
}
};
Why can't I use the second predicate with filter()?
It looks like your filter function's predicate parameter is not properly contravariant. Try rewriting it as follows:
public static <T> List<T> filter(Collection<? extends T> source,
Predicate<? super T> predicate)
{
final List<T> result = new ArrayList<T>(source.size());
for (T element: source)
if (predicate.apply(element))
result.add(element);
return result;
}
That says that so long as the predicate function is willing to accept a type no narrower than type T, calling it with an instance of type T (or some type further derived from T) will work fine.
Try generifying isNonNull:
private static class IsNonNullPredicate<T> implements Predicate<T> {
public boolean apply(T item) {
return null != item;
}
}
Now you can return it through a generic method in your util class instead of a constant.
public <T> Predicate<T> isNonNull() {
return new IsNonNullPredicate<T>();
}
Alternatively, just do an unchecked cast on a stored instance instead of creating a new one each time:
private final Predicate isNotNullPredicate = new IsNonNullPredicate();
public <T> Predicate<T> isNonNull() {
return (Predicate<T>) isNotNullPredicate;
}
This is what the Collections class in the Java Collections library does to provide support for generics in its utility methods. Before 1.5 there was Collections.EMPTY_LIST which after generics were added would return a List<Object>. However, that wouldn't give back a suitably generified list so Collections.emptyList() was added to return a List of any type that would fit the calling context.

Type safe heterogeneous container pattern to store lists of items

I'm trying to implement a type-safe heterogeneous container to store lists of heterogeneous objects.
I have seen several exameples of type-safe heterogeneous container pattern (link) but all of them store a single object of a type.
I have tryed to implement it as follows:
public class EntityOrganizer {
private Map<Class<?>, List<Object>> entityMap = new HashMap<Class<?>, List<Object>>();
public <T> List<T> getEntities(Class<T> clazz) {
return entityMap.containsKey(clazz) ? entityMap.get(clazz) : Collections.EMPTY_LIST;
}
private <T> void addEntity(Class<T> clazz, T entity) {
List<T> entityList = (List<T>) entityMap.get(clazz);
if(entityList == null) {
entityList = new ArrayList<T>();
entityMap.put(clazz, (List<Object>) entityList);
}
entityList.add(entity);
}
}
But the problem is this code is full of unchecked casts. Can someone help with a better way of implementing this?
Many thanks
The question is, what is "unchecked cast"?
Sometimes casts are provably safe, unfortunately the proof is beyond javac's capability, which does only limited static analysis enumerated in the spec. But the programmer is smarter than javac.
In this case, I argue that these are "checked casts", and it's very appropriate to suppress the warning.
See 2 other related examples:
Heterogeneous container to store genericly typed objects in Java
Typesafe forName class loading
You don't have to cast :
(List<T>) entityMap.get(clazz).
When you say
entityMap.get(clazz)
you actually have a List<Object> which is enough for your needs.
The same for
entityList = new ArrayList<T>();
You should just use entityList = new ArrayList<Object>();
Your type safety is ensured by the method declaration
<T> void addEntity(Class<T> clazz, T entity) {
and the use of Map having as key a Class.
So the code should look like :
private <T> void addEntity(Class<T> clazz, T entity) {
List<Object> entityList = entityMap.get(clazz);
if(entityList == null) {
entityList = new ArrayList<Object>();
entityMap.put(clazz, entityList);
}
entityList.add(entity);
}
For very small lists you can encode a linked list of java generics.
And<UUID, And<Integer, Of<String>>> x = Tuple.of("test").and(2).and(UUID.randomUUID());
The definition of the types for And and Of are a bit mind bending. For brevity I've left out equals/hashCode.
import java.util.Objects;
import java.util.UUID;
public abstract class Tuple<T extends Tuple<T>> {
public static final <E> Of<E> of(E e) {
return new Of<>(e);
}
public abstract <E> And<E, T> and(E e);
public static final class And<T, R extends Tuple<R>> extends Tuple<And<T, R>> {
public final T t;
public final R r;
private And(T t, R rest) {
this.t = t;
this.r = rest;
}
public <N> And<N, And<T, R>> and(N next) {
return new And<>(next, this);
}
}
public static final class Of<T> extends Tuple<Of<T>> {
public final T t;
private Of(T t) {
this.t = t;
}
public <N> And<N, Of<T>> and(N next) {
return new And<>(next, this);
}
}
}

Categories

Resources