Can I make this java pluck () method more secure?

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 this idea from underscore.js . Use case:

 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 is mistaken, an exception is not thrown until the caller tries to get the object from the list. Ideally, I would like to throw a ClassCastException from the pluck method pluck . However, I see no way to access the list type at runtime.

Is there any trick I can use to make sure the caller does not have an invalid list?


Edit: Thus, using the feedback received, the safe implementation will be as follows:

 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 lambdai seems to be doing what I wanted, so I think I will use this. Thank you Mike!

Disclaimer: LambdaJ ( @GoogleCode | @GitHub ). This project is no longer supported since the release of JDK8 ( JSR 335 , JEP 126 ).

+6
source share
8 answers

You can change your signature as follows:

 public static <T, F> List<F> pluck(String fieldName, Class<F> fieldType, List<T> list, Class<T> listType) 

You have a list type and a field type.

+2
source

Why do not you define the signature as follows:

 public static <T, U> List<T> pluck(String fieldName, Class<T> fieldType, List<U> list); 

It:

1) Make the client specify the type of field that he wants to "wrest" so that you can perform the correct type checking in your method.

2) Make the client provide a general list from which to "pluck", so you prevent the use of another source of errors (the client that provides the list containing objects of different types).

I think it is as safe as possible.

+2
source

What do you mean by an invalid list? If you mean that they are trying to do something, then do not try to change the declaration to public static <T> List<T> pluck(String fieldName, List<T> list) .

I am confused by the comment. However, I don't see a way to access the type of the list on run time. . 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 is working, and then turns it into regular drops, as before. This was necessary so that they felt the need for backward and forward compatibility.

+1
source

You should use generics for the type parameter and pass a 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; } 

Typically, when you get a warning about an insecure click on a type parameter, you should see if you can replace it with a call to Class.cast

+1
source

you can try the one set by the Google collection library instead of saving 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(); } } 
+1
source

Using the Google Guava Collections2.transform() Library, you can use Collections2.transform() .

Using

Given an interface / class, for example. 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 get a list from each Entity identifier.

 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 thing you can get. This satisfies the proper principles of OOP and Java 5-7.

In Java 8, you can achieve the same effect with 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()); } 
+1
source

Not sure what you are asking for, but you can try:

 Class c = list.get(0).getClass(); if (!c.equals(Person.class)) throw new ClassCastException(); 
0
source

You can pass the list to java.lang.reflect.ParameterizedType and check that the array returned by getActualTypeArguments() contains the class you need. Also, you're out of luck.

0
source

Source: https://habr.com/ru/post/906944/


All Articles