DaveAKing / guava-libraries

Automatically exported from code.google.com/p/guava-libraries
Apache License 2.0
0 stars 0 forks source link

Make method TypeToken.toGenericType() public #1645

Closed GoogleCodeExporter closed 9 years ago

GoogleCodeExporter commented 9 years ago
I don't know if this is even possible, but it seems like it should be...

I'd like the ability to take the output of TypeToken.toString() and parse it 
back into the equivalent TypeToken.

For example, one should be able to say:

   foo = TypeToken.parse("java.util.List<java.lang.String>");

and have foo equal to new TypeToken<List<String>>() {}.

NOTE 1: it seems reasonable to have the restriction that no type variables are 
allowed (would it even make sense to allow them? If you did you'd have to have 
some way of deciding whether "T" was a type variable or a class name).

NOTE 2: a ClassLoader should probably be a parameter to the parse method

I tried to implement this myself but it seems impossible without using 
package/private methods or generating bytecode (if it's possible to implement 
this idea using the standard public TypeToken API, please advise on how it 
would be done).

For example, this doesn't work:

    TypeVariable tv = List.class.getTypeParameters()[0];
    System.out.println(new TypeResolver().where(
        tv, String.class).resolveType(List.class));

prints:

    java.util.List

instead of:

    java.util.List<String>

Also can't use TypeToken.of(List.class).getTypes(), which returns:

    java.util.List, java.util.Collection<E>, java.lang.Iterable<E>

instead of:

    java.util.List<E>, java.util.Collection<E>, java.lang.Iterable<E>

The special case for the first item in the list seems odd (is the raw "List" 
type really a sub-type of Iterable<E>?)

What seems to be stopping this is the ability to convert a raw class into its 
parameterized Type or TypeToken. E.g., wish there was a method like this:

    public Type Class.getGenericType();

That is, this feature request could be answered by adding a new method that 
does the equivalent, e.g.:

    public TypeToken<? extends T>  TypeToken.getGenericType();

So that e.g. TypeToken.of(List.class).getGenericType() -> List<E>

I'm guessing all of this is impossible for some reason I don't understand or it 
would already be there....

Thanks.

Original issue reported on code.google.com by archie.c...@gmail.com on 24 Jan 2014 at 9:56

GoogleCodeExporter commented 9 years ago

Original comment by lowas...@google.com on 24 Jan 2014 at 10:09

GoogleCodeExporter commented 9 years ago
Snooping through the code, it looks like such a method as suggested at the end 
of comment #1 already exists:

    static <T> TypeToken<? extends T> TypeToken.toGenericType()

Any reason this method can't be made public?

Original comment by archie.c...@gmail.com on 24 Jan 2014 at 10:15

GoogleCodeExporter commented 9 years ago
Just to clarify things a bit here, as this issue has evolved and the issue 
title is no longer accurate.

The original request for a String parser is no longer needed... I can do that 
part myself, but need TypeToken.toGenericType() to made public in order for it 
to be feasible.

So instead what is being requested here is simply that 
TypeToken.toGenericType() be made public.

This method provides useful functionality that is not available anywhere else; 
and you can consider the original string parsing question as a motivating 
example (i.e., how could you implement string parsing without it?).

Original comment by archie.c...@gmail.com on 27 Jan 2014 at 4:46

GoogleCodeExporter commented 9 years ago
Please change the title of this feature request to:

  Make method TypeToken.toGenericType() public

Thanks.

Original comment by archie.c...@gmail.com on 22 Feb 2014 at 9:29

GoogleCodeExporter commented 9 years ago

Original comment by cpov...@google.com on 25 Feb 2014 at 4:54

GoogleCodeExporter commented 9 years ago
Exposing toGenericType() sounds reasonable.

Although can you show us how it's going to be used in this use case? I wonder 
if it can be done with existing utilities such as where(), getSupertype(), 
getSubtype() etc. 

Original comment by be...@google.com on 25 Jun 2014 at 8:57

GoogleCodeExporter commented 9 years ago
As mentioned above, the original motivating example is a TypeToken parser 
(converts String -> TypeToken).

I tried to implement one using all available public API's but it seemed 
impossible.

The root problem is that e.g. TypeToken.of(List.class) returns 
"java.util.List", not "java.util.List<E>". Because of this, there's no way to 
take an arbitrary Class object representing a parameterized type, create a 
corresponding TypeToken, and then set the type parameters for the TypeToken.

However, if TypeToken.toGenericType() is available then that makes it easy.

You can see the current implementation of the parser here:

  https://code.google.com/p/jsimpledb/source/browse/trunk/src/java/org/jsimpledb/util/TypeTokenParser.java

Currently it uses reflection to access Types.newParameterizedType() directly 
(note, it invokes Types.newParameterizedType() instead of 
TypeToken.toGenericType() because the parser doesn't handle arrays).

I could be completely missing something... if so, I'd be interested to see how 
you'd rewrite TypeTokenParser.java using only existing public API's.

Thanks.

Original comment by archie.c...@gmail.com on 26 Jun 2014 at 12:11

GoogleCodeExporter commented 9 years ago
I know the dependency is kinda weird. But ignore that for a moment, if you had 
Guice's Types.newParamterizedType(), would you still have needed 
toGenericType()?

Were you trying to use toGenericType() to implement newParameterizedType()?

Although it doesn't seem easy to do though? For example, when parsing 
"List<String>", after calling List.class.toGenericType(), you have 
TypeToken<List<E>>, but there isn't any easy way to turn that into List<String> 
unless you use reflection to get the type variable <E> and do substitution 
using TypeResolver.

Original comment by be...@google.com on 27 Jun 2014 at 1:26

GoogleCodeExporter commented 9 years ago
Oh. I guess you sort of implied it's what you are going to do: use 
clazz.getTypeParameters() to get the type vars and use TypeResolver to 
substitute. So never mind my previous question then.

It still seems like newParameterizedType() is what's really needed.

It was asked before that newParameterizedType() be exposed. That Guice already 
has it was one of the reasons we haven't done that, yet.

Original comment by be...@google.com on 27 Jun 2014 at 1:48

GoogleCodeExporter commented 9 years ago
Exposing newParameterizedType() would work. I don't currently use Guice, so 
that option would be inconvenient. Seems like guava should have it's own 
version for completeness' sake, though that's a matter of opinion I suppose.

Thanks.

Original comment by archie.c...@gmail.com on 27 Jun 2014 at 2:21

GoogleCodeExporter commented 9 years ago
Okay. Whether it's possible to use Guice was what I was curious to know.

I think you have a perfectly reasonable use case here.

I'm torn between the two options:

1. Just expose newParameterizedType(). But then we overlap with Guice. And it'd 
be weird not to also open up newArrayType(), subtypeOf(), supertypeOf() etc.

2. Expose toGenericTye(). It doesn't immediately solve the issue, but it makes 
it easier to do because you can then use TypeResolver to implement 
newParameterizedType(). And who knows, there may be other use cases of it.

Or, maybe even more directly:

3. Just offer a TypeToken parser.

Hmm...

Original comment by be...@google.com on 27 Jun 2014 at 2:40

GoogleCodeExporter commented 9 years ago
This issue has been migrated to GitHub.

It can be found at https://github.com/google/guava/issues/<issue id>

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:10

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 1 Nov 2014 at 4:17

GoogleCodeExporter commented 9 years ago

Original comment by cgdecker@google.com on 3 Nov 2014 at 9:07