Why can't you have multiple interfaces in a generic wildcard?

I know all kinds of counter-intuitive properties of common Java types. Here, in particular, I do not understand, and I hope that someone can explain to me. When specifying a type parameter for a class or interface, you can bind it so that it implements several interfaces using the public class Foo<T extends InterfaceA & InterfaceB> . However, if you instantiate the actual object, this no longer works. List<? extends InterfaceA> List<? extends InterfaceA> great, but List<? extends InterfaceA & InterfaceB> List<? extends InterfaceA & InterfaceB> cannot compile. Consider the following full snippet:

 import java.util.List; public class Test { static interface A { public int getSomething(); } static interface B { public int getSomethingElse(); } static class AandB implements A, B { public int getSomething() { return 1; } public int getSomethingElse() { return 2; } } // Notice the multiple bounds here. This works. static class AandBList<T extends A & B> { List<T> list; public List<T> getList() { return list; } } public static void main(String [] args) { AandBList<AandB> foo = new AandBList<AandB>(); // This works fine! foo.getList().add(new AandB()); List<? extends A> bar = new LinkedList<AandB>(); // This is fine too // This last one fails to compile! List<? extends A & B> foobar = new LinkedList<AandB>(); } } 

It seems that the semantics of bar should be clearly defined - I can’t think of any type of loss of security, allowing the intersection of two types, not just one. I am sure there is an explanation. Does anyone know what it is?

+63
java generics bounded-wildcard language-design
Jul 10 '11 at 19:00
source share
5 answers

Interestingly, the java.lang.reflect.WildcardType interface looks like it supports both upper bounds and lower bounds for the arg argument; and each can contain several borders

 Type[] getUpperBounds(); Type[] getLowerBounds(); 

This is much more than the language allows. There's a hidden comment in the source code

 // one or many? Up to language spec; currently only one, but this API // allows for generalization. 

The author of the interface seems to think this is a random limitation.

The canned answer to your question: generics are already too complicated as they are; adding extra complexity may be the last straw.

In order for the wildcard to have several upper bounds, you need to scan the specification and make sure that the whole system is still working.

One problem that I know will be in type inference. Current withdrawal rules simply cannot deal with interception types. There is no rule to reduce the A&B << C limit. If we reduced it to

  A<<C or A<<B 

any current output engine must undergo a major overhaul to allow such a bifurcation. But the real serious problem is that it allows you to use several solutions, but there is no reason to prefer each other.

However, no withdrawal is required for security output; we can simply refuse output in this case and ask the programmer to explicitly fill in the type arguments. Therefore, the difficulty in deriving is not a strong argument against the types of interception.

+32
Jul 11 '11 at 3:19
source share

From the Java language specification :

4.9 Intersection types The intersection type takes the form T1 and ... and Tn, n> 0, where Ti, 1in, are type expressions. Intersection types arise in the processes of capture conversion (§ 5.1.10) and type inference (§15.12.2.7). It is not possible to write an intersection type directly as part of a program; The syntax does not support this . Intersection type values ​​are those objects that are values ​​of all Ti types, for 1in.

So why is this not supported? I guess what you should do with such a thing? - suppose this is possible:

 List<? extends A & B> list = ... 

Then what should

 list.get(0); 

return? There is no syntax to get the return value of A & B Adding something to such a list would not be possible, so this is basically useless.

+21
Jul 10 '11 at 20:15
source share

No problem ... just declare the type you need in the method signature.

This compiles:

 public static <T extends A & B> void main(String[] args) throws Exception { AandBList<AandB> foo = new AandBList<AandB>(); // This works fine! foo.getList().add(new AandB()); List<? extends A> bar = new LinkedList<AandB>(); // This is fine too List<T> foobar = new LinkedList<T>(); // This compiles! } 
+8
Jul 10 2018-11-11T00:
source share
Good question. It took me a while to understand.

Let's simplify your case: you are trying to do the same thing as if you declared a class that extends 2 interfaces, and then a variable that has type 2 of the interface, something like this:

  class MyClass implements Int1, Int2 { } Int1 & Int2 variable = new MyClass() 

Of course, it’s illegal. And this is equivalent to what you are trying to do with generics. What you are trying to do is:

  List<? extends A & B> foobar; 

But then, to use foobar, you will need to use the variable of both interfaces as follows:

  A & B element = foobar.get(0); 

Which means not legal in Java. This means that you declare list items as two types at the same time, and even if our brains can handle it, the Java language cannot.

+1
Jul 10 '11 at 20:12
source share

What is it worth: if someone is interested in this because they really would like to use it in practice, I worked on it, defining an interface that contains the union of all the methods in all the interfaces and class that I work with. that is, I tried to do the following:

 class A {} interface B {} List<? extends A & B> list; 

which is illegal - so instead I did this:

 class A { <A methods> } interface B { <B methods> } interface C { <A methods> <B methods> } List<C> list; 

This is still not as useful as being able to enter something like List<? extends A implements B> List<? extends A implements B> , for example. if someone adds or removes methods in A or B, the input of the list will not be updated automatically, this requires a manual change of C. But this worked for my needs.

0
Apr 10 '17 at 23:15
source share



All Articles