thmayr
thmayr

Reputation: 57

Calling method from a generic wildcard reference

the following code is reduced to the essentials for the question:

public class GenericTest 
{
    private interface CopyInterface<T extends CopyInterface<T>>
    {
        public void copyFrom(T source);
    }

    public void testMethod()
    {
        CopyInterface<?> target;
        CopyInterface<?> source;
        target.copyFrom(source);
    }
}

This leads to the following compiler error message:

The method copyFrom(capture#1-of ?) in the type GenericTest.CopyInterface is not applicable for the arguments (GenericTest.CopyInterface) GenericTest.java /ACAF/src/de/tmasoft/acaftest/attributes line 14 Java Problem

When I use the raw type for the variable target, I just get a raw-type warning. Is there a way to get this compiled without using a raw-type?

Upvotes: 3

Views: 285

Answers (4)

thmayr
thmayr

Reputation: 57

It is clear for me, that the compiler cannot check if source and target are of the same type. But I thought I can solve it with an explicit cast and not with raw types. So I found now the following solution, which seems to work:

public class GenericTest
{
    private interface CopyInterface<T extends CopyInterface<?>>
    {
        public void copyFrom(T source);
    }

    private class A implements CopyInterface<A>
    {
        @Override
        public void copyFrom(A source)
        {}
    }

    public static void copy(CopyInterface<?>                        source,
                            CopyInterface<? super CopyInterface<?>> target)
    {
        target.copyFrom(source);
    }

    @SuppressWarnings("unchecked")
    public void testMethod()
    {
        CopyInterface<?> target = new A();
        CopyInterface<?> source = new A();
        ((CopyInterface<? super CopyInterface<?>>) target).copyFrom(source);
        copy(source, (CopyInterface<? super CopyInterface<?>>) target);
    }
}

But I must admit, that I don't completely understand the difference between

interface CopyInterface<T extends CopyInterface<?>> 

and

interface CopyInterface<T extends CopyInterface<T>>

Upvotes: 0

newacct
newacct

Reputation: 122439

The bound on the parameter of CopyInterface is useless. CopyInterface doesn't care what things can be copied from. It's testMethod that cares how copyFrom is used. So you want to make testMethod() a generic method, which imposes restrictions on what its type parameter must support. (Of course, in a real example, T must be used in a parameter or return type of testMethod for it to be meaningful.)

private interface CopyInterface<T>
{
    public void copyFrom(T source);
}

public <T extends CopyInterface<? super T>> void testMethod()
{
    T target;
    T source;
    target.copyFrom(source);
}

Upvotes: 1

Edwin Dalorzo
Edwin Dalorzo

Reputation: 78579

The problem is that your source type is not compatible with the type of the target type, but that is what your method declaration requires.

Your copyFrom method is declared as

public void copyFrom(T source);

Which means that the generic type argument for the object in target must be the same as that of the source, namely both must be of some type T.

Of course, if you use a wildcard type like ? there is no way the compiler can tell if the ? is of the same type T in both source and target, after all it could be different, for instance.

CopyInterface<?> target = new Foo();
CopyInterface<?> source = new Bar();

Above ? represents two different types CopyInterface<Foo> and CopyInterface<Bar>. So, as you can see, when you use ? you lose type information that later that compiler don't have available to make important decisions and you get those compiler errors.

Coming back to the copyFrom method, in a case like this the compiler cannot know if the ? corresponds to the type T that the method copyFrom(T source) expects, hence your compiler error.

You would not have this problem if both source and target were of the same type.

For instance, if you had an implementation of your interface like this:

class Foo implements CopyInterface<Foo> {
 @Override public void copyFrom(Foo source){}
}

Then you would not have problem doing this:

Foo target = new Foo();
Foo source = new Foo();
target.copyFrom(source);

In this case T is Foo in both source and target, and this is compatible with the signature of your method.

Since it looks like you only read from the source, then you could also relax the rules in your interface by declaring it as follows:

interface CopyInterface<T extends CopyInterface<T>> {
   void copyFrom(CopyInterface<? extends T> source);
}

Then your implementation might look like

class Foo implements CopyInterface<Foo> {
   @Override public void copyFrom(CopyInterface<? extends Foo> source){}
}

Then you could do

CopyInterface<Foo> target = new Foo();
CopyInterface<Foo> source = new Foo();
target.copyFrom(source);

But as long as you use wildcard types here, I doubt you can manage to make it work.

Upvotes: 2

Misha
Misha

Reputation: 28133

You have to make your testMethod() generic and declare that source and target are of the same type:

public <T extends CopyInterface<T>> void testMethod() {
    T target = ...; //initialize the variable
    T source = ...; //initialize the variable
    target.copyFrom(source);
}

Upvotes: 4

Related Questions