Java: Help me understand: How to use interface met

2019-08-29 11:20发布

问题:

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?

回答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());


回答2:

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>.



回答3:

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)