Carl
Carl

Reputation: 445

Java type capture for inner classes

I have the following (obviously useless in this form) Java class:

public class EntrySet<T> {

    public static class Entry<T> {
        T obj;
    }

    private final Set<Entry<T>> set;

    public EntrySet() {
        this.set = Sets.newHashSet();
    }

    public Set<Entry<T>> getEntries() {
        return set;
    }
}

Given this class, the following does not compile:

EntrySet<?> entrySet = new EntrySet<SomeClass>();
Set<EntrySet.Entry<?>> entries = entrySet.getEntries();

The second line has the compile error "cannot convert from Set<EntrySet.Entry<capture#1-of ?>> to Set<EntrySet.Entry<?>>". I cannot find a way to eliminate this error, even with casting or using a helper function.

What exactly is the issue here, and is there a way to get a clean compile?

The best I could do is:

EntrySet<?> entrySet = new EntrySet<SomeClass>();
Set<?> tmp = entrySet.getEntries();
Set<EntrySet.Entry<?>> entries = (Set<EntrySet.Entry<?>>) tmp;

which is obviously terrible (even with the relevant warning suppression).

Upvotes: 1

Views: 134

Answers (2)

clinton
clinton

Reputation: 612

The unbounded wild card <?> is like the super-type of all generic types. It is similar to what an Object class is to all java classes. The only operations that you can perform on a unbounded wildcard reference are those which take a 'null' (if <?> parameter is used in the argument reference) or those that return a 'Object' (if <?> parameter is used in the return type reference). So your code will compile if it returns a reference of the type Object.

 class EntrySet<T> {

    public static class Entry<T> {
        T obj;
    }

    private final java.util.Set<Entry<T>> set;

    public EntrySet() {
        this.set = new java.util.HashSet();
    }

    public java.util.Set<Entry<T>> getEntries() {
        return set;
    }

    public static void main(String [] args){
        EntrySet<?> entrySet = new EntrySet<SomeClass>();

    //java.util.Set<EntrySet.Entry<?>> entries = entrySet.getEntries(); // Error

         Object o = entrySet.getEntries();         // OK !
    }
}

class SomeClass{}

Upvotes: 0

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147154

Removing entrySet you are attempting to assign a Set<EntrySet.Entry<SomeClass>> to Set<EntrySet.Entry<?>>. There are hundreds of Stack Overflow questions which are essentially the same thing. You could add a EntrySet.Entry<OtherClass> to the latter but not the former, so that would break type-safety.

Probably the way to deal with this is to capture the wild card by calling a method with a (named) generic parameter.

    EntrySet<?> entrySet = new EntrySet<SomeClass>();
    fn(entrySet);
...
private static <T> void fn(EntrySet<T> entrySet) {
    Set<EntrySet.Entry<T>> entries = entrySet.getEntries();

Upvotes: 1

Related Questions