Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Generics" discover function #92

Closed
fborriello opened this issue Sep 18, 2019 · 9 comments
Closed

"Generics" discover function #92

fborriello opened this issue Sep 18, 2019 · 9 comments
Assignees
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed unsolvable The issue has no applicable solutions

Comments

@fborriello
Copy link
Contributor

Description

A really useful function, this project should offer, is the possibility to figure out the "Generic" type of a Collection or a Map.
This function is also required to complete the implementation of a new Map Transformer (for more details please refer to issue: #88)

Desired solution
The ideal solution would be the implementation of a new method: getGenericType that takes in input any kind of Java Collection or Map and returns its Generic type(s).

e.g.

List<String> v = new ArrayList<String>();
Class<?> genericClazz = getGenericType(v.getClass()); // will return: String

and

Map<String, Integer> v = new HashMap<String, Integer>();
Pair<Class<?>, Class<?>> typeMap = getGenericType(v.getClass()); // will return a Pair Class<?> keyClass = typeMap.getLeft(); // will return: String
Class<?> elemClass = typeMap.getRight(); // will return: Integer

Additional context

The method should be added into the utility class: ReflectionUtil

@fborriello fborriello added enhancement New feature or request help wanted Extra attention is needed good first issue Good for newcomers labels Sep 18, 2019
@fborriello fborriello added this to To do in BULL:: Roadmap via automation Sep 18, 2019
@CodeFarmer
Copy link

I may be missing something, but I suspect the example as written is not solvable due to type erasure.

If you had a collection that contained some elements, you could introspect them and guess what the generic type might have been, by trying to guess a least-generic common (super)type for the collection elements?

@polarene polarene self-assigned this Sep 24, 2019
@polarene
Copy link
Contributor

I think this might be done because generic type arguments are retained in subclasses. I'll have a try at it!

@antimoroma
Copy link
Contributor

antimoroma commented Sep 24, 2019

Hi @CodeFarmer, actually i have take a look on this issue and yes erasure as usual create this type of problem.
With some trick i was able to get the type of collection (let me say List) but not the type of data contained, or for better explain we can reach the type of this data with too much trick (i dind't test this way until the end) (trick is anyway the same idea of @polarene :D)

Anyway, i think to use a simple solution that seems working fine. Simply getting the first element X1 of a non empty Collection and get the type of Object X1 and the problem seems resolved (but still to be implemented apart a simple POC).
Unique issue could be if the collections is defined as a Collection of Object but this the worst case that we didn't want to support.
I think this is a good idea, feel free to discuss it, or if you have time to contribute to this implementation

@fborriello fborriello moved this from To do to In progress in BULL:: Roadmap Sep 24, 2019
@CodeFarmer
Copy link

CodeFarmer commented Sep 24, 2019

@antimoroma your solution is a start (if you are willing to ignore the empty list case in the first example, which @fborriello might not want).

However, you will need to look at all the objects in the collection, as they may not all be the same type (say ArrayList[Employee] containing objects that are Manager and Engineer classes), and find the most-specific superclass that expresses all of the elements.

@polarene
Copy link
Contributor

Hi guys, after some tries, also involving @antimoroma in the end, I realized that this particular use case is unsolvable just using reflection. The trick I knew was described in this post: https://xebia.com/blog/acessing-generic-types-at-runtime-in-java/
but it works only for subtypes closing on actual type parameters. For example, if we had:

class Names implements List<String> {...}

Names n = new Names();
Class<?> cls = getGenericType(n.getClass());

then we could recover the actual type parameter String. But when the function is handled a List<String>, even if the instance has a generic superclass, the type parameter is not closed over at compile time (eg: new ArrayList<String>()) so the type information is unknown and the method will return a type E, as declared in the source of AbstractList.

In conclusion, the only possible way seems that of looking up the element's type as you suggested above.

@antimoroma
Copy link
Contributor

antimoroma commented Sep 24, 2019

@CodeFarmer for empty list i'm not the owner so we refer to @fborriello as you said, but the feature is needed to copy data from list, so (in this particular case i think) we can not take in charge this List or Map if it's empty could be and easy solution, we genereate an empty list on output.

Good point about the observation about subclass in List (may be we need to investigate more about this i think in the mean time) , and i agree absolutely with @polarene last message

@fborriello
Copy link
Contributor Author

I do agree with you all. This function is actually required for the Map Transformation module to figure out the key and the element type to understand how to get them transformed.
The solution, you proposed, to discover the type from the first element of a collection/map could work. In case the collection/map would be empty we will return an empty List/Map.
I’ll try with that solution and then get back to you. Thanks

@fborriello fborriello added the unsolvable The issue has no applicable solutions label Sep 25, 2019
BULL:: Roadmap automation moved this from In progress to Done Sep 25, 2019
@polarene
Copy link
Contributor

polarene commented Oct 14, 2019

Yes @fborriello, that could be the only way to go. Keep in mind that it will be simple if you have an empty collection or map, or if the elements are homogeneous (List<String>). However, we could have more difficult cases if the collection has type bounds, such as List<? extends Number> or List<? super Integer>, that is if the type parameter is covariant or contravariant.
In the first case, you'll have to inspect all the elements and find the greatest supertype (you don't know beforehand it's Number).
In the second case, you must resolve to Object since the upper bound is open.
The information about type parameter bounds is available through reflection.

This can get quite complicated, I know, but in the end maybe we could decide we don't want to go through all this hassle and just make Bull raise an exception if it finds a bounded type parameter.

@antimoroma
Copy link
Contributor

Yes, i think it could be useful to fix some prerequisite for this implementation (or for a first implementation):

  • Simply we support Collection or List without bounds (covariant, controvariant), in this case, we throw an exception.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers help wanted Extra attention is needed unsolvable The issue has no applicable solutions
Projects
Development

No branches or pull requests

4 participants