2

I have a method call in a loop currently that does not compile:

for (Example example : Util.getExample(List.class)) {
    // Do something with example
}

Util:

public class Util {
    public <T> T getExample(Class<T> clazz) {
        //...
    }
}

The obvious fix is to cast the return from getExample to List<Example>. I'm wondering: is there is an alternative way to avoid the cast?

Further Information:

Posters asked for more information, so here goes...

I have built a framework around annotation processing that writes code to access and mutate class members (constructors, fields and methods). This framework backs both Parceler and Transfuse and allows me to, during compilation, identify a property and generate code to access or modify said property. For private properties (private constructors, private fields, private methods) I use a utility to perform these actions (Parceler's, Transfuse's) to break encapsulation via reflection.

Parceler has a feature to unroll collections during serialization in order to serialize members of the given collection. For private collections the InjectionUtil is used to access these properties within a loop:

for (Example example : InjectionUtil.getField(List.class, Container.class, container, "exampleField")) {
    // ...
}

Which is the bug I'm currently faced with, and thus, why I'm asking about avoiding a cast. I'd prefer to not cast as I'd like to generically generate a bit of code to access a type and respect Java generics in the process.

John Ericksen
  • 10,995
  • 4
  • 45
  • 75
  • As I understand the question, you could do this with something like Guava's `TypeToken`, because you could pass a `TypeToken>` instead of `List.class` which is a `Class`. – Radiodef May 18 '17 at 04:27
  • http://stackoverflow.com/questions/20773850/gson-typetoken-with-dynamic-arraylist-item-type – soorapadman May 18 '17 at 04:28
  • Ah, great idea @Radiodef, I'll try this out. – John Ericksen May 18 '17 at 04:35
  • I had a feeling that this would turn out to involve serialization. Generic type-checking happens at compile time, and serialization happens at run time, so they don't really go together at all. Do you really need a compile-time type-safe `List` if you're just going to write it out as bytes? Something like `List extends Parcelable>` might work just as well. – Wyzard May 18 '17 at 23:02
  • Right, Parceler is an annotation processor (runs at compile time) that generates a Parcelable for use during runtime. List is handled, but would suffer from the same issue. – John Ericksen May 18 '17 at 23:49

3 Answers3

4

If your getExample method is supposed to always return a list, then yes, change its return type to List<T>. But since you're passing List.class as an argument, it looks like you want to have a method that can return both lists and non-lists depending on which class object you pass it.

If so, that's not going to work the way you might be hoping. Your method in this case returns just List, the raw type. To make it return List<Example>, you'd have to pass it something like a hypothetical List<Example>.class, but there's no such thing. Generic type parameters are erased at compile time, so List<Example> and List<String> are really both the same class; they don't have separate class objects, so a class object argument can't tell your method what kind of list it should return.

You'll probably need to try a different design approach. Since this is clearly a simplified example, you might be able to get more help if you post more details about what you're actually trying to accomplish.

Wyzard
  • 33,849
  • 3
  • 67
  • 87
  • You're right @Wyzard, I have simplified my problem. Really trying to brainstorm around this problem before trying some drastically different approaches. – John Ericksen May 18 '17 at 04:35
  • I've updated the question with more information. I hope this is enough context to support the problem I'm facing. – John Ericksen May 18 '17 at 16:26
2

Guava's TypeToken can be used in this case because List<Foo>.class is not valid. TypeToken is used by creating an anonymous class. Because anonymous classes keep their generic signatures, this works.

for (Example foo : Util.getExample(new TypeToken<List<Example>>() {}) {
    // do stuff
} 

// utils
public <T> T getExample(TypeToken<T> typeToken) {
    Type type = typeToken.getType();
    // get example
}

TypeToken is more specific than just using the Class. You could also use the plain Type as a parameter so you can still feed it a Class. This is how Gson does it.

killjoy
  • 3,665
  • 1
  • 19
  • 16
0

I think this is a design issue...

Since the method in Util you are calling is called getExamples it seems reasonable that it might just as well be fixed to return some collectionwhose elements are instance of the Example class.

It is reasonable to change getExamples to something like this?:

class Util {

  public static <C extends Collection<? supper Example>> getExamples(final Supplier<C> factory) {
       final C result = factory.get();
       // here goes the code that adds the examples to the result collection
       // using add or addAll.
       return result;
  } 

}

So for-example if you wan to get a List<Example> using ArrayList<E> for implementation you would do like so:

List<Example> examples = Util.getExamples(ArrayList<Example>::new);

Try to pass the returned collection class object reference instead (eg. List.class, ArrayList.class) won't work as the code in getExamples will have a hard time (a) figuring out how to call the appropriate constructor using reflexion to instantiate the result (kinda of impossible if you pass just an interface class object such as List.class) and (b) casting the return from a raw type into a generic type with Example as element type. The latter is trivial however it is not as neat as it can be as it will generate a warning.

It is just more straight forward to delegate in the using code to indicate explicitly how to instantiate the result collection.

If you break away from returning a Collection and use methods like add and addAll in getExamples then perhaps you should borrow the Collectors framework from the java stream API.

Valentin Ruano
  • 2,726
  • 19
  • 29