Java: Help me understand: How to use interface methods on a bounded wildcard field?
Asked Answered
J

3

0

I'm having trouble understanding why I can use bounded wildcards like this, if I can't (seem to) make any (genericly-typed) use of it.

If I have a wildcard field in a class, I can't use any of the methods with generic parameters of the implemented interface (unless I supply null as the argument).

class SomeClass {}

class DerivedClass extends SomeClass {}

interface IInterf<T extends SomeClass> {
    T returnsT();
    void paramT(T parm);
    T paramAndReturnT(T parm);
    int nonGenericMethod(int x);
}

class Impl {
    protected IInterf<?> field; //this is bound to <extends SomeClass>
                                //- it's implied by the definition 
                                //  of IInterf which is bound
                                // but what's the point?

    public Impl(IInterf<? extends SomeClass> var){
        field = var;
    }
    public void doSmth(){

        SomeClass sc = field.returnsT();  //works

        field.paramT(new SomeClass());
          //error: method paramT in interface IInterf<T> cannot be applied to given types;
          //required: CAP#1
          //found: SomeClass
          //reason: actual argument SomeClass cannot be converted to CAP#1 by method invocation conversion
          //where T is a type-variable:
          //  T extends SomeClass declared in interface IInterf
          //where CAP#1 is a fresh type-variable:
          //  CAP#1 extends SomeClass from capture of ? 

         field.paramT(null); //works

        SomeClass sc2 = field.paramAndReturnT(new DerivedClass());
          //error: method paramAndReturnT in interface IInterf<T> cannot be applied to given types;
          // SomeClass sc2 = field.paramAndReturnT(new DerivedClass());           //required: CAP#1
          //found: DerivedClass
          //reason: actual argument DerivedClass cannot be converted to CAP#1 by method invocation conversion
          //where T is a type-variable:
          //  T extends SomeClass declared in interface IInterf
          //where CAP#1 is a fresh type-variable:
          //  CAP#1 extends SomeClass from capture of ?            
          //
        int x = field.nonGenericMethod(5);  //obviously works.
    }
}

FWIW, I couldn't convince the C# compiler to accept something similar.

Am I missing something?

Jovanjove answered 12/1, 2013 at 15:55 Comment(0)
P
1

When you declare field as

protected IInterf<?> field;

the ? stands for an unknown class that extends SomeClass. Think of it not a s a wildcard but as a particular class deriving SomeClass but anonymous.

If you now try to call

field.paramT(new SomeClass());

this fails because a SomeClass instance is not compatible to what the ? is standing for, namely the anonymous class that extends SomeClass.

There is no problem to use null, this is compatible with any class.

Exactly the same happens with

SomeClass sc2 = field.paramAndReturnT(new DerivedClass());
Piteous answered 12/1, 2013 at 16:3 Comment(4)
Your explanation is not entirely correct: when using protected IInterf<?> field; even field.paramAndReturnT(new DerivedClass()); will fail to compile even though DerivedClass extends SomeClass.Emmen
@Emmen Where did I say it would compile? The ? stands for an unknown class that extends SomeClass. This unknown class has nothing to do with DerivedClass except that both happen to have SomeClass as a superclass.Piteous
You did not, but I inferred that from the "what the ? is standing for, namely a class that extends SomeClass." since DerivedClass does extend SomeClass — it's a bit more complicated than that (see my answer).Emmen
@praseodym: Henry is correct. His explanation is very clear. The ? stands for some unknown subtype of SomeClass, i.e. it is some subclass and you don't know, and can't know, what it is. i.e. the compiler can replace it with any subclass of SomeClass (e.g. DerivedClass or even SomeBogusSubclassIMadeUp) and it will work. DerivedClass cannot be passed because it is not assignable to SomeBogusSubclassIMadeUpKielty
I
0

You are right, you can't use these methods. Often, you don't need these methods (for example, you add something <? extends T> to a collection <T>. It makes sense to use them if you don't need more information. If you need to call these methods, you can't use wildcards. Instead, you could do <T extends SomeClass>.

Impresario answered 12/1, 2013 at 16:3 Comment(0)
E
0

You're trying to use generics/wildcards where they are not needed. Instead, either of the following will work:

1) Define the interface so that it accepts any class, and limit the types when using the interface:

interface IInterf<T> {}
protected IInterf<SomeClass> field;

2) Define the interface so that it accepts classes that extend SomeClass, and use the interface without specifying extra type information:

interface IInterf<T extends SomeClass> {}
protected IInterf field;

As for why the wildcard does not work: ? extends SomeClass means an unknown subtype of SomeClass. Since we don't know what the type is, we don't know if it is a supertype of SomeClass (or DerivedClass in the second method call); it might or might not be such a supertype, so it isn't safe to pass SomeClass (or DerivedClass). (from the Java Wildcards documentation)

Emmen answered 12/1, 2013 at 16:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.