Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: Wildcard Types Mismatch Results in Compilation Error

I've created a factory class in my project which would allow me (in theory) to create managers for any (supported) given type. Interacting with the manager allows me to alter certain properties of a given type. The problem I'm facing is when I attempt to create a manager for a generic type, the compiler crushes my hopes and dreams.

The following code is a stripped down version of what I'm working with. The line where I attempt to create my 'test3Manager' will not compile and I'm trying to understand why this is the case. The lines below it shows a 'workaround', which I'm trying to avoid.

    import java.util.List;

    public class GenTest {
        public static void main(String[] args) {
            String test1 = "";
            IRandomType<String> test2 = null;
            IAnotherRandomType<?> test3 = null;

            IManager<String> test1Manager = Factory.createManager(test1);
            IManager<IRandomType<String>> test2Manager = Factory.createManager(test2);
            IManager<IAnotherRandomType<?>> test3Manager = Factory.createManager(test3); // Doesn't compile. Why?

            // Work around?
            IManager<?> test3ManagerTmp = Factory.createManager(test3);
            IManager<IAnotherRandomType<?>> test3Manager2 = (IManager<IAnotherRandomType<?>>) test3ManagerTmp;
        }

        public interface IRandomType<T> {}
        public interface IAnotherRandomType<T> {}
        public interface IManager<T> {}

        public static class Factory {
            public static <T> IManager<T> createManager(T object) {
                return null;
            }
        }
    }

The exact compile error message is:

    Type mismatch: cannot convert from GenTest.IManager<GenTest.IAnotherRandomType<capture#1-of ?>> to GenTest.IManager<GenTest.IAnotherRandomType<?>>

Similar questions have been asked before (see below); however, I don't know if this question is considered a duplicate of them. I only state this since I'm having trouble inferring answers from these questions to mine. I'm hoping someone could clarify what I'm doing wrong with my use of generics.

Related questions on SO are:

  • Bounded-wildcard related compiler error
  • Incompatible wildcard types that should be compatible
like image 243
jdknight Avatar asked Oct 02 '22 12:10

jdknight


1 Answers

Use the following:

IManager<IAnotherRandomType<?>> test3Manager =
        Factory.<IAnotherRandomType<?>>createManager(test3);

This is just a case of the compiler's type inference falling on its face, so it's necessary explicitly provide the type argument for T.

More technically:

test3 is declared to have the type IAnotherRandomType<?>, where ? is a wildcard capture - a sort of one-use type parameter representing some specific unknown type. That's what the compiler's referring to when it says capture#1-of ?. When you pass test3 into createManager, T gets inferred as IAnotherRandomType<capture#1-of ?>.

Meanwhile, test3Manager is declared to have the type IManager<IAnotherRandomType<?>>, which has a nested wildcard - it does not behave like a type parameter but rather represents any type.

Since generics aren't covariant, the compiler can't convert from IManager<IAnotherRandomType<capture#1-of ?>> to IManager<IAnotherRandomType<?>>.

More reading on nested wildcards:

  • Multiple wildcards on a generic methods makes Java compiler (and me!) very confused
  • Java Generic List<List<? extends Number>>
  • Which super-subset relationships exist among wildcards?
like image 177
Paul Bellora Avatar answered Oct 07 '22 19:10

Paul Bellora