Dreando
Dreando

Reputation: 589

Java generics - unexpected situation with Optional wrapping generics

I have faced following situation:

import com.google.common.base.Optional;

public class CovarianceTest {

    class Race {
    }

    class Dog<Race> {
    }

    public Optional<Dog<? extends Race>> getDog() {
        Dog<? extends Race> dogWithSomeRace = new Dog();
        return Optional.of(dogWithSomeRace);
    }
}

Situation like this causes compilation problem. The IDE says:

Incompatible types.
Required: Optional<Dog<? extends Race>>
Found: Optional<? extends Dog<? extends Race>

Which is clearly wrong.

Could someone explain why this is happening?

Upvotes: 1

Views: 236

Answers (2)

Witold Kaczurba
Witold Kaczurba

Reputation: 10505

This is your code:

  1. Compiler thinks Race (in class Dog<Race>) is a type parameter that you will use for further parametrization of class Dog.
  2. In the assignment Dog<? extends Race> dogWithSomeRace = new Dog(); this is considered as not best practice.

enter image description here

I believe the code below is what you are trying to do. Use T as type parameter. Here we go:

import com.google.common.base.Optional;

public class CovarianceTest {

    class Race {
    }

    class Dog<T> {
        void whatever(T t) {
            System.out.println(t);
        }
    }

    public Optional<Dog<? extends Race>> getDog() {
        Dog<? extends Race> dogWithSomeRace = new Dog<Race>();
        return Optional.of(dogWithSomeRace);
    }

    public static void main(String[] args) {
        System.out.println((new CovarianceTest()).getDog().isPresent());
        System.out.println((new CovarianceTest()).getDog().get().getClass());
    }
}

This prints nicely:

true
class CovarianceTest$Dog

Upvotes: 2

SME_Dev
SME_Dev

Reputation: 1890

It seems you confused the class' generic type declaration with the boundary declaration of a generic type argument.

In your case:

class Race {}

class Dog<Race> {} // "Race" is declared as a generic type in dog. It's not referring to your Race class in any way.

Your definition of Dog is equivalent to

class Dog<E> {}

If you want to set a subclass of Race to be a generic boundary for Dog, then do for example

class Dog<E extends Race> {}

You could also refine the method's generic declarations:

public Optional<Dog<? extends Race>> // this could be shortend to Dog<?>, because Dog's boundary is already limited to Race in the class signature
                                     getDog() {
    Dog<? extends Race> dogWithSomeRace // This can be shortend aswell to Dog<?> 
                                        = new Dog(); // Missing type declaration in the constructor call 
    return Optional.of(dogWithSomeRace);
}

Upvotes: 2

Related Questions