Michel
Michel

Reputation: 372

Why does this not work while this does ? (Java Generics, named generic type vs. unnamed)

I'm sorry but I don't know how to express my question in another way than by showing an example :

public interface IStuff<GenericParameter>{}

public interface IWorkWithStuff<GenericParameter>
{
    void doSomethingWithStuff(IStuff<GenericParameter> stuff);
}

public interface IBoth<GenericParameter>
extends IStuff<GenericParameter>, IWorkWithStuff<GenericParameter>
{}

public class Test<Both extends IBoth<?>>
{
    Both _myBoth;
    void test(final Both otherBoth)
    {
        _myBoth.doSomethingWithStuff(otherBoth);
    }
}

This does not compile, could someone explain why ? The error is :

The method doSomethingWithStuff(IStuff) in the type IWorkWithStuff is not applicable for the arguments (Both)

On the other hand, if I name the parameter, it works :

public class Test<NamedParameter, Both extends IBoth<NamedParameter>>
{
    Both _myBoth;
    void test(final Both otherBoth)
    {
        _myBoth.doSomethingWithStuff(otherBoth);
    }
}

It seems quite similar to me (except that this second solution is not practicable to me in the real case I'm having this problem), can someone explain how this is different ?

Thanks a bunch !


I add that I tested with Java 1.6 and Java 1.8

Edit

Answer from awsome gave me a solution.

In the link he pointed there is a section names "Capture helpers" explaining a way to avoid such problems.

In my case, this code works :

public class WorkingTest<Both extends IBoth<?>>
{
    Both _myBoth;

    void test(final Both otherBoth)
    {
        final IBoth<?> myBoth = _myBoth;
        final IBoth<?> _otherBoth = otherBoth;
        rebox(myBoth, _otherBoth);
    }

    protected <Something, SomethingElse> void rebox(final IBoth<Something> both, final IBoth<SomethingElse> otherBoth)
    {
        both.doSomethingWithStuff(both);
    }
}

It works when types are valid and fails when types are not.

Thanks !

Edit

Wooops, there is a mistake in my "solution" :

I wrote

        both.doSomethingWithStuff(both);

instead of

        both.doSomethingWithStuff(otherBoth);

which doesn't work (and makes sens).

The only solution I found for now is to use cast :

public class WorkingTest<Both extends IBoth<?>>
{
    Both _myBoth;

    public WorkingTest(final Both myBoth)
    {
        _myBoth = myBoth;
    }

    void test(final Both otherBoth)
    {
        deboxrebox(_myBoth, otherBoth);
    }

    @SuppressWarnings("unchecked")
    protected <CommonParent> void deboxrebox(final Both first, final Both second)
    {
        final IBoth<CommonParent> _first = (IBoth<CommonParent>) first;
        final IBoth<CommonParent> _second = (IBoth<CommonParent>) second;
        _first.doSomethingWithStuff(_second);
    }
}

At least, it encapsulate the cast, but still isn't very satisfying.

Do you think that using "capture helpers" a better solution can be found ?

Upvotes: 3

Views: 140

Answers (2)

awsome
awsome

Reputation: 2153

Here is a little modification to your example to understand the issue you are facing

    public class Test<Both extends IBoth<?>> {

    IBoth<?> hello;

    void test(final Both otherBoth) {
        hello.doSomethingWithStuff(hello);  // The method doSomethingWithStuff(IStuff<capture#1-of ?>) in the type IWorkWithStuff<capture#1-of ?> is not applicable for the arguments (IBoth<capture#2-of ?>)
        hello.doSomethingWithStuff(hello); // The method doSomethingWithStuff(IStuff<capture#3-of ?>) in the type IWorkWithStuff<capture#3-of ?> is not applicable for the arguments (IBoth<capture#4-of ?>)
    }
}

interface IStuff<S> {
}

interface IWorkWithStuff<T> {
    void doSomethingWithStuff(IStuff<T> stuff);
}

interface IBoth<U> extends IStuff<U>, IWorkWithStuff<U> {
}

I have also written the errors with the method calls for doSomethingWithStuff. You see that everytime a new call is made capture#xxx changes. The number xxx here says that this is a new unknown type. More about wildcards can be read here http://www.ibm.com/developerworks/java/library/j-jtp04298/index.html

Upvotes: 1

Bilbo Baggins
Bilbo Baggins

Reputation: 3019

The difference is between the type when you specify ? it is kind of a wild card, while in case of you are specifying a type from which the class Test is extending is predefined i.e. mentioned at compile time, as the java checks for type at compile time. In your first attempt the type is not defined. and hence it is showing an error.

Following link also contains good explanation on the generics.

Check the meaning of ?

Upvotes: 0

Related Questions