-
Notifications
You must be signed in to change notification settings - Fork 93
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
Reification #46
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.
|
@@ -182,6 +237,14 @@ public static void disableCache() { | |||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test would be better if the type argument was something other than Object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved in fc7cffb
|
||
return changed ? new ResolvedParameterizedType(parameterizedType, resolvedTypeArguments) : parameterizedType; | ||
} | ||
throw new UnsupportedOperationException( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same re: the unsupported message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved in fc7cffb
@@ -182,6 +237,25 @@ public static void disableCache() { | |||
return resolveRawArguments(resolveGenericType(type, subType), subType); | |||
} | |||
|
|||
/** | |||
* 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should document a @throws UnsupportedOperationException
that describes what is unsupported
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same re: @throws UnsupportedOperationException
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Resolved in fc7cffb
@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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
Type
and 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.