Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
565 views
in Technique[技术] by (71.8m points)

generics - 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:

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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:


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...