Andrey Bulgakov
Andrey Bulgakov

Reputation: 443

Incompatible types: inferred type does not conform to upper bound(s)

I was implementing some architecture when I saw the following error:

Error:(33, 55) java: incompatible types: inferred type does not conform to upper bound(s)
    inferred: java.io.Serializable
    upper bound(s): sandbox.ExpirePolicy,java.io.Serializable

The whole simplified code is below:

interface Configuration<K,V>{}
interface ExpirePolicy{}
interface Factory<T>{}


class FactoryBuilder {
    public static <T extends Serializable> Factory<T> of(T instance){
        System.out.println(instance.getClass());
        return new Factory<T>() {};
    }
}

class BaseConfiguration<K,V> implements Configuration<K,V> {
    public BaseConfiguration<K,V> setExpiryPolicyFactory(Factory<? extends ExpirePolicy> factory){
        return this;
    }
}

class C<K,V> extends BaseConfiguration<K,V> {
    public C<K,V> setExpiration(){
        super.setExpiryPolicyFactory(FactoryBuilder.of((Serializable) getExpirePolicy()));
        return this;
    }

    private ExpirePolicy getExpirePolicy(){
        return new ExpirePolicy() {};
    }
}

The exception is in trying to call setExpiryPolicyFactory(Factory<? extends ExpirePolicy> factory) with instance of Factory<Serializable>

But if i delete generic in extends BaseConfiguration<K,V> the program will be successfully compiled.

So the next declaration of class C is correct:

class C<K,V> extends BaseConfiguration {
    public C<K,V> setExpiration(){
        super.setExpiryPolicyFactory(FactoryBuilder.of((Serializable) getExpirePolicy()));
        return this;
    }

    private ExpirePolicy getExpirePolicy(){
        return new ExpirePolicy() {};
    }
}

The question is: why the second implementation(of class C) will be successfully compiled and the first not?

UPD:

Simpler example of question (delete <T> from extends Base<T>) and program compiles well :

class Base<T> {
    public void test(ArrayList<? extends CharSequence> list) {}
}

class Derived<T> extends Base<T> {
    public void callTest() {
        super.test(new ArrayList<Integer>());
    }
}

Upvotes: 1

Views: 3710

Answers (2)

Ilya Zakharov
Ilya Zakharov

Reputation: 21

When you delete <T> from extends Base<T> statement, the Base class starts to be treated as a raw type.

According to Java spec:

The supertype of a class may be a raw type. Member accesses for the class are treated as normal, and member accesses for the supertype are treated as for raw types. In the constructor of the class, calls to super are treated as method calls on a raw type.

This means that super.test(...) call is also treated as method call on a raw type as if it has been declared like:

public void test(ArrayList list) {}

Thus no compilation errors happens.

Upvotes: 1

Mick Mnemonic
Mick Mnemonic

Reputation: 7956

It seems like the factory builder should take in an ExpirePolicy instead of Serializable for creating the factory. Changing the signature to

class FactoryBuilder {
    public static <T extends ExpirePolicy> Factory<T> of(T instance){
        System.out.println(instance.getClass());
        return new Factory<T>() {};
    }
}

enables using

class C<K,V> extends BaseConfiguration<K,V> {

      public C<K,V> setExpiration(){
          super.setExpiryPolicyFactory(FactoryBuilder.of(getExpirePolicy()));
          return this;
      }

      private ExpirePolicy getExpirePolicy(){
          return new ExpirePolicy() {};
      }
}

without extra casts.

The second implementation of C compiles, but with warnings, because it's using raw types.

Upvotes: 0

Related Questions