Why Can't You Have Multiple Interfaces in a Bounded Wildcard Generic

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

Interestingly, interface java.lang.reflect.WildcardType looks like it supports both upper bounds and lower bounds for a wildcard arg; and each can contain multiple bounds

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

This is way beyond what 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 consider that this is an accidental limitation.

The canned answer to your question is, generics is already too complicated as it is; adding more complexity might prove to be the last straw.

To allow a wildcard to have multiple upper bounds, one has to scan through the spec and make sure the entire system still works.

One trouble I know would be in the type inference. The current inference rules simply can't deal with intersection types. There's no rule to reduce a constraint A&B << C. If we reduced it to

    A<<C 
or
A<<B

any current inference engine has to go through major overhaul to allow such bifurcation. But the real serious problem is, this allows multiple solutions, but there's no justification to prefer one over another.

However, inference is not essential to type safety; we can simply refuse to infer in this case, and ask programmer to explicitly fill in type arguments. Therefore, difficulty in inference is not a strong argument against intercection types.

Java Generics With a Class & an Interface - Together

Actually, you can do what you want. If you want to provide multiple interfaces or a class plus interfaces, you have to have your wildcard look something like this:

<T extends ClassA & InterfaceB>

See the Generics Tutorial at sun.com, specifically the Bounded Type Parameters section, at the bottom of the page. You can actually list more than one interface if you wish, using & InterfaceName for each one that you need.

This can get arbitrarily complicated. To demonstrate, see the JavaDoc declaration of Collections#max, which (wrapped onto two lines) is:

public static <T extends Object & Comparable<? super T>> T
max(Collection<? extends T> coll)

why so complicated? As said in the Java Generics FAQ: To preserve binary compatibility.

It looks like this doesn't work for variable declaration, but it does work when putting a generic boundary on a class. Thus, to do what you want, you may have to jump through a few hoops. But you can do it. You can do something like this, putting a generic boundary on your class and then:

class classB { }
interface interfaceC { }

public class MyClass<T extends classB & interfaceC> {
Class<T> variable;
}

to get variable that has the restriction that you want. For more information and examples, check out page 3 of Generics in Java 5.0. Note, in <T extends B & C>, the class name must come first, and interfaces follow. And of course you can only list a single class.

Understanding generics syntax in java - generic types implementing multiple interfaces as parameters

Unlike TypeParameter, TypeArgument can't have several bounds, so <? extends ItfA & ItfB> is not valid.

Here's my guess on why it is so.

I think the reason is that TypeArgument needs to be specific to be useful. For instance in case of Class<T> the method cast returns T, you should be able to declare a variable of that type and assign the result to it.

TypeArgument can be:

  1. ClassType
  2. InterfaceType
  3. TypeVariable
  4. ArrayType
  5. Wildcard

Cases 1-4 are not problematic, there you always have some specific type or type variable.

In case of the Wildcard we have WildcardBounds for instance <? extends IntfA>.

If you allow just one upper bound, then you have something specific for T. In case of Class<? extends IntfA> you can assume that cast returns InfA.

If you allow more that one upper bound <? extends ItfA & ItfB> then the question is, what could you use for T then? Since you in general case you don't have any specific type for T, the best you can do is Object which is not very useful.

I think this is the reason language authors did not allo multiple upper bounds for wildcard type arguments.

Java generics - Make Generic to extends 2 interfaces

Reimeus already pointed out that what you're asking for in your edit isn't possible. I'd just like to expand a little on why.

One would think you could use the following:

public <T, U extends T & IDisposable> void mapThis(
Class<? extends MyClass<T>> key,
Class<? extends U> value
) { ... }

In fact that's what came to my mind when I first saw this post. But this actually gives a compiler error:

a type variable may not be followed by other bounds

To help me explain why, I'd like to quote an Oracle Blogs post by Victor Rudometov about this error:

This fact is not always clear, but it is true. The following code
should not compile:

interface I {}

class TestBounds <U, T extends U & I> {

}

Because JLS Chapter 4
Types, Values, and Variables section 4.4 Type Variables states: "The
bound consists of either a type variable, or a class or interface type
T possibly followed by further interface types I1 , ..., In.
". So one
may use T extends U, T extends SomeClass & I, but not T extends U & I.
This rule applies to all cases including type variables and bounds in
methods and constructors.

The reasons for this restriction are explored in a closely related post: Why can't I use a type argument in a type parameter with multiple bounds?

To summarize, the restriction was imposed in order to "preclude certain awkward situations coming into existence" (JLS §4.9).

What kind of awkward situations? An answer by Chris Povirk describes one:

[A reason for the restriction is] the possibility of specifying illegal types. Specifically, extending a generic interface twice with different parameters. I can't come up with a non-contrived example, but:

/** Contains a Comparator<String> that also implements the given type T. */
class StringComparatorHolder<T, C extends T & Comparator<String>> {
private final C comparator;
// ...
}

void foo(StringComparatorHolder<Comparator<Integer>, ?> holder) { ... }

Now holder.comparator is a Comparator<Integer> and a Comparator<String>.

Chris also points to Sun bug 4899305, which was a bug contesting this language restriction. It was closed as Won't Fix with the following comment:

If a type variable could be followed by type variables or by (possibly
parameterized) interfaces, there would likely be more mutually
recursive type variables, which are very difficult to handle. Things
are already complicated when a bound is simply a parameterized type,
e.g. <S,R extends Comparable<S>>. Consequently, bounds are not going
to change now. Both javac and Eclipse agree that S&T and
S&Comparable<S> are illegal.

So those are the reasons behind the restriction. Addressing generic methods specifically (which your question concerns), I'd like to further point out that type inference would theoretically cause such bounds to be pointless anyway.

If we reexamine the type parameters declared in the hypothetical signature above:

<T, U extends T & IDisposable>

Assuming the caller isn't explicitly specifying T and U, this can be reduced to the following:

<T, U extends Object & IDisposable>

Or just this (subtle difference, but that's another topic):

<T, U extends IDisposable>

This is because T doesn't have any bounds, so no matter what type of arguments get passed in, T can always resolve to Object at the very least, and so then can U.

Let's go back and say T is bounded:

<T extends Foo, U extends T & IDisposable>

This can be reduced in the same way (Foo could be a class or interface):

<T extends Foo, U extends Foo & IDisposable>

Based on that reasoning, the syntax you're trying to achieve is pointless as far as restricting the caller to more specific arguments.

Pre-Java 8 addendum:

Prior to Java 8, there is a use case for what you're trying to do. Because of a limitation with how the compiler infers generic method type parameters, my above reasoning to go out the window. Take the following generic method:

class MyClass {
static <T> void foo(T t1, T t2) { }
}

This is a common beginner's mistake of trying to make a method that takes two parameters of the "same type". Of course it's pointless because of the way inheritance works:

MyClass.foo("asdf", 42); // legal

Here, T is inferred to be Object - this matches up with earlier reasoning about simplifying the mapThis type parameters. You have to manually specify the type parameters in order to achieve the intended type checking:

MyClass.<String>foo("asdf", 42); // compiler error

However, and here's where your use case starts to come in, it's a different matter with multiple type parameters with staggered bounds:

class MyClass {
static <T, U extends T> void foo(T t, U u) { }
}

Now this call errors:

MyClass.foo("asdf", 42); // compiler error

The tables have turned - we have to manually relax the type parameters to get it to compile:

MyClass.<Object, Object>foo("asdf", 42); // legal

This happens because of the limited way in which the compiler infers method type parameters. For this reason, what you wanted to achieve would've actually had an application in restricting the caller's arguments.

However, this problem appears to have been fixed in Java 8, and MyClass.foo("asdf", 42) now compiles without any error (thanks to Regent for pointing this out).

Why this Java generic code with bounded wildcard doesn't compile?

Suppose someone uses your class this way:

List<Double> list = new ArrayList<>();
X<List<Double>> x = new X<>();
x.someMethod(list);

Now your method will try to add a Long to a List<Double>. That's why the compiler won't allow it.

You cannot add a Long to a List<? extends Number>, since a List<? extends Number> can be assigned a List<Double>, a List<Integer>, etc...

You can change your class declaration to X<T extends List<Number>>, which will solve the compilation error, since to a List<Number> you can always add a Long (or an instance of any other sub-class of Number).

Combining bounded wildcards in Java

class Foo<S extends Comparable & Clonable>

Should work.

See Java Generics Wildcarding With Multiple Classes for further info.

[1]:

Java Generics: Multiple Inheritance in Bounded Type Parameters T extends A & I

This doesn't do what you expect it to. T extends A & I indicates that the caller can specify any type that extends A and I, and you'll return it.

Casting bounded wildcard to unbounded wildcard within a generic type is an error (XY? extends T to XY?

Even though a Class<? extends T> is a Class<?>, a Set<Class<? extends T>> is not a Set<Class<?>>, for the same reason that even though a Dog is an Animal, a List<Dog> is not a List<Animal>. Here, ? extends T has the same role as Dog, and ? has the same role as Animal.

You'll need a ? extends in front of Class to get this to compile correctly.

Set<? extends Class<?>> sku = sk;

Wildcard Bounds if Class Generic Type is Already Bounded?

1) I'd assume that these lists can contain the exact same objects, namely instances of Bar of any generic type. Is this true?

Both lists can only contain Bar<T> where T is either Foo or any of its children.

2) Which type is preferred?

Both are equivalent in this case. However, reading code is more important than writing it; users might think "Oh, it's an unbounded wildcard, so I can use any type.", but they'd be wrong due to the bounded wildcard of Bar. For that reason, the latter may be preferred in some cases, but at the end of the day, it's your call.



Related Topics



Leave a reply



Submit