1

what i'm trying to do is as follows: assume that getClassFromString() and getAllObjectsFromRepositoryByClass() already exist.
why can't i use Class<T extends Named & HasId>.

i tried generifying the class itself, but can't use stuff like T.class etc.

public interface Named   {    String getDisplayName(); }
public interface HasId   {    String getId();          }


public class Foo {

public List<PairItem>  getPairItems(String typeId) {

    Class<T extends Named & HasId> clazz = getClassFromString(typeId);

    List<T> allObjects = getAllObjectsFromRepositoryByClass(clazz);

    List<PairItem> items = new ArrayList<>();
    for (clazz obj : allObjects) {
        items.add(obj.getDisplayName(),ibj.getId());
    }

    return items;
}         
Nati
  • 1,034
  • 5
  • 19
  • 46
  • Is there any reason why you need two interfaces? Can't you just have one interface `HasNameAndId` with two methods `getDisplayName()` and `getId()`? What really confuses me about your code is that you seem to be trying to call the two interface methods on a `Class` object. You can't do that. You can only call those methods on instances of those interfaces. – Paul Boddington Jan 01 '15 at 11:33
  • @pbabcdefp - can't use them as one interface. it was a mistake, of course - the List is of type T and not clazz. – Nati Jan 01 '15 at 11:42
  • Ok, well you can create a third interface `public interface HasNameAndId extends Named, HasId {}`, and then you can have a `List`. – Paul Boddington Jan 01 '15 at 11:48
  • Maybe http://stackoverflow.com/questions/745756/java-generics-wildcarding-with-multiple-classes is interesting to you – TheConstructor Jan 01 '15 at 12:00
  • @TheConstructor ive seen this before, but couldn't find a way to make any use of it. i can't generify Foo since i instantiate it with a string. besides, i cant pass T.class as parameter to `getAllObjectsFromRepositoryByClass()` as well – Nati Jan 01 '15 at 12:22
  • You can just add `` to `getAllObjectsFromRepositoryByClass()` and have it return `List` from `Class` that is the way to pass type-parameters around. – TheConstructor Jan 01 '15 at 12:26

1 Answers1

0

You can change you Foo-class in this way:

public class Foo {

    public <T extends Named & HasId> List<PairItem> getPairItems(String typeId) {
        Class<?> classFromString = getClassFromString(typeId);
        // Java 8 seems to be unable to chain asSubclass-calls. These are merely to verify our unchecked cast
        classFromString.asSubclass(Named.class);
        classFromString.asSubclass(HasId.class);
        //noinspection unchecked
        return getPairItems((Class<T>) classFromString);
    }

    public <T extends Named & HasId> List<PairItem> getPairItems(final Class<T> clazz) {

        List<T> allObjects = getAllObjectsFromRepositoryByClass(clazz);

        List<PairItem> items = new ArrayList<>();
        for (T obj : allObjects) {
            items.add(new PairItem(obj.getDisplayName(), obj.getId()));
        }

        return items;
    }

}

This fixes your problems with multiple boundaries as they are only allowed for type-parameters per documentation.; also I guess that

If one of the bounds is a class, it must be specified first.

leads to the problem that the asSubclass()-calls can not be chained, otherwise we could remove our unchecked cast.

The second method can profit from the streaming-API like this:

    public <T extends Named & HasId> List<PairItem> getPairItems(final Class<T> clazz) {
        List<T> allObjects = getAllObjectsFromRepositoryByClass(clazz);

        return allObjects.stream()
                         .map(obj -> new PairItem(obj.getDisplayName(), obj.getId()))
                         .collect(Collectors.toList());
    }

Overall I assumed that you wanted to instantiate PairItem and split the method so there is a unchecked and a fully-checked part.

TheConstructor
  • 4,285
  • 1
  • 31
  • 52