Reification#46
Conversation
5e8016c to
d1dbf2b
Compare
Codecov Report
@@ Coverage Diff @@
## master #46 +/- ##
============================================
- Coverage 78.99% 74.76% -4.23%
- Complexity 81 94 +13
============================================
Files 1 1
Lines 238 321 +83
Branches 73 95 +22
============================================
+ Hits 188 240 +52
- Misses 17 39 +22
- Partials 33 42 +9
Continue to review full report at Codecov.
|
| return resolveRawArguments(resolveGenericType(type, subType), subType); | ||
| } | ||
|
|
||
| public static <T, S extends T> Type resolveType(Class<T> type, Class<S> subType) { |
There was a problem hiding this comment.
Looking for inspiration on how to document this new public method...
| return substituteGenerics(resolveGenericType(type, subType), getTypeVariableMap(subType, null)); | ||
| } | ||
|
|
||
| public static Type resolveType(Type type, Class<?> context) { |
There was a problem hiding this comment.
Looking for inspiration on how to document this new public method...
867c0ca to
c78617f
Compare
| assert typeArguments[0] instanceof ParameterizedType; | ||
| ParameterizedType firstTypeArgument = (ParameterizedType) typeArguments[0]; | ||
| assert firstTypeArgument.getRawType() == HashSet.class; | ||
| assert firstTypeArgument.getActualTypeArguments()[0] == Object.class; |
There was a problem hiding this comment.
This test would be better if the type argument was something other than Object.
There was a problem hiding this comment.
Well, in this case there's no explicit upper bound for the wildcard type, but I still wanted to re-use the existing fixture. I would like to keep this test, and making it "better" would require a different fixture.
Maybe we can add another one transports better what you want. So you want one case where a wildcard type is resolved to something below Object? I added this in aa744c9. If that's not what you mean, please give clearer instructions.
There was a problem hiding this comment.
So you want one case where a wildcard type is resolved to something below Object?
Yea, I thought that would be more clear.
I would like to keep this test, and making it "better" would require a different fixture.
Good point.
| assert typeArguments[1] instanceof ParameterizedType; | ||
| ParameterizedType secondTypeArgument = (ParameterizedType) typeArguments[1]; | ||
| assert secondTypeArgument.getRawType() == ArrayList.class; | ||
| assert secondTypeArgument.getActualTypeArguments()[0] == Object.class; |
There was a problem hiding this comment.
Same here re: Object, and in a few places below.
| private final Type[] resolvedTypeArguments; | ||
|
|
||
| private ResolvedParameterizedType(ParameterizedType original, Type[] resolvedTypeArguments) { | ||
| Objects.requireNonNull(original); |
There was a problem hiding this comment.
TypeTools is released for Java 1.6, so we can't use this API :)
|
|
||
| @Override | ||
| public int hashCode() { | ||
| int result = Objects.hash(original); |
There was a problem hiding this comment.
Same here re: the 1.6 API limitation.
| if (upperBounds.length == 1 && lowerBounds.length == 0) { | ||
| return substituteGenerics(upperBounds[0], typeVariableMap); | ||
| } | ||
| throw new UnsupportedOperationException( |
There was a problem hiding this comment.
UnsupportedOperationException is good to throw here, but I don't like embedding the issue URL in the exception. A brief message about why this operation is unsupported is enough.
|
|
||
| return changed ? new ResolvedParameterizedType(parameterizedType, resolvedTypeArguments) : parameterizedType; | ||
| } | ||
| throw new UnsupportedOperationException( |
There was a problem hiding this comment.
Same re: the unsupported message.
|
|
||
| /** | ||
| * The same as {@link #substituteGenerics(Type, Class)}, but first resolves the generic type of {@code type}. | ||
| * This is a convenience method. |
There was a problem hiding this comment.
We should document a @throws UnsupportedOperationException that describes what is unsupported
There was a problem hiding this comment.
Well, this method is just pure convenience for consuming the API, I would like to avoid documenting it with anything more than just stating this and point the reader at the "real" method.
There was a problem hiding this comment.
Ah, sure. I meant that both of them should be documented.
| * @param context the class that serves as starting point to resolve replacements of type variables | ||
| * @return a type that is structurally the same as {@code type}, except that type variables have been replaced | ||
| */ | ||
| public static Type substituteGenerics(Type type, Class<?> context) { |
There was a problem hiding this comment.
Same re: @throws UnsupportedOperationException
|
@lorenzleutgeb This is very nice. Using
Maybe you can explain this problem a bit more? In general I'm interested to consider good reasons why we shouldn't just offer something like |
| * The same as {@link #substituteGenerics(Type, Class)}, but first resolves the generic type of {@code type}. | ||
| * This is a convenience method. | ||
| */ | ||
| public static <T, S extends T> Type substituteGenerics(Class<T> type, Class<S> subType) { |
There was a problem hiding this comment.
A name that came to mind for me is "reifyGenerics", which seems appropriate here :)
|
First off, I agree with most comments, and even was expecting some. I will address most of them ASAP.
Assume
The reason I am even PRing this is that I want to improve some other library. This other library is lacking support for resolution of type variables. If it sees On the contrary, type tokens introduce a new type like Maybe I am misunderstanding something, but this is my reasoning for preferring the proposed approach.
Probably |
This is a fair enough point. The last thing I'd like to resolve is naming. How about |
Of course, naming is always hardest... I agree to call it "reify" in some way, but I dislike I am repeating myself, but the essence of this contribution is that it just takes a type and substitutes all To emphasize this, I added a version with just one parameter and no context, see here. Another viewpoint would be that all type variables and wildcards inside a type and its referenced types are its parameters. Then calling it What about just calling it |
Yep, good point. |
f048c58 to
5bafe91
Compare
|
OK. I think that all your remarks are resolved, please have another look and consider merging. Thank you. |
|
This was a really thorough PR - thanks @lorenzleutgeb! Will cut a new release shortly... |
This is another attempt to resolve #8, meant to supersede #12.
Different from what @jhalterman originally proposed, this approach does /not/ use type tokens. Rather it traverses common implementations of
Typeand replaces type variables in there recursively. The benefit I see doing this, is that it is compatible with the existing type hierarchy. A generic parameterized type is just resolved to look like a concrete parameterized type.