Why can't I use a type argument in a type parameter with multiple bounds?

So, I understand that the following doesn't work, but why doesn't it work?

interface Adapter<E> {}

class Adaptulator<I> {
    <E, A extends I & Adapter<E>> void add(Class<E> extl, Class<A> intl) {
        addAdapterFactory(new AdapterFactory<E, A>(extl, intl));
    }
}

The add() method gives me a compile error, "Cannot specify any additional bound Adapter<E> when first bound is a type parameter" (in Eclipse), or "Type parameter cannot be followed by other bounds" (in IDEA), take your pick.

Clearly you're just Not Allowed to use the type parameter I there, before the &, and that's that. (And before you ask, it doesn't work if you switch 'em, because there's no guarantee that I isn't a concrete class.) But why not? I've looked through Angelika Langer's FAQ and can't find an answer.

Generally when some generics limitation seems arbitrary, it's because you've created a situation where the type system can't actually enforce correctness. But I don't see what case would break what I'm trying to do here. I'd say maybe it has something to do with method dispatch after type erasure, but there's only one add() method, so it's not like there's any ambiguity...

Can someone demonstrate the problem for me?


I'm also not sure why the restriction is there. You could try sending a friendly e-mail to the designers of Java 5 Generics (chiefly Gilad Bracha and Neal Gafter).

My guess is that they wanted to support only an absolute minimum of intersection types (which is what multiple bounds essentially are), to make the language no more complex than needed. An intersection cannot be used as a type annotation; a programmer can only express an intersection when it appears as the upper bound of a type variable.

And why was this case even supported? The answer is that multiple bounds allow you to control the erasure, which allows to maintain binary compatibility when generifying existing classes. As explained in section 17.4 of the book by Naftalin and Wadler, a max method would logically have the following signature:

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

However, this erases to:

public static Comparable max(Collection coll)

Which does not match the historical signature of max, and causes old clients to break. With multiple bounds, only the left-most bound is considered for the erasure, so if max is given the following signature:

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

Then the erasure of its signature becomes:

public static Object max(Collection coll)

Which is equal to the signature of max before Generics.

It seems plausible that the Java designers only cared about this simple case and restricted other (more advanced) uses of intersection types because they were just unsure of the complexity that it might bring. So the reason for this design decision does not need to be a possible safety problem (as the question suggests).

More discussion on intersection types and restrictions of generics in an upcoming OOPSLA paper.

Why can't Trump explain what Obamagate is?, What is Obamagate? And why can't President Trump explain it? Also this week, how does an election work in a pandemic? As the virus  When you get a message that says Windows can't be activated, there are several possible reasons why. Select any of the following topics to see some typical reasons for activation difficulty, along with possible ways to get going again.


Two possible reasons for outlawing this:

  1. Complexity. Sun bug 4899305 suggests that a bound containing a type parameter plus additional parameterized types would allow for even more complicated mutually recursive types than already exist. In short, Bruno's answer.

  2. 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>. It's not clear to me exactly how much trouble this would cause for the compiler, but it's clearly not good. Suppose in particular that Comparator had a method like this:

void sort(List<? extends T> list);

Our Comparator<Integer> / Comparator<String> hybrid now has two methods with the same erasure:

void sort(List<? extends Integer> list);
void sort(List<? extends String> list);

It's for these kinds of reasons that you can't specify such a type directly:

<T extends Comparator<Integer> & Comparator<String>> void bar() { ... }
java.util.Comparator cannot be inherited with different arguments:
    <java.lang.Integer> and <java.lang.String>

Since <A extends I & Adapter<E>> allows you to do the same thing indirectly, it's out, too.

Why can't we use antibody tests for diagnosing COVID-19 yet?, Nearly two million antibody tests imported into Australia can't be used to diagnose COVID-19. But it's difficult to make an antibody test that is  Why Can't I? (Original Version) Artist Liz Phair; Licensed to YouTube by UMG (on behalf of Hollywood Records); LatinAutor - Warner Chappell, LatinAutor - SonyATV, LatinAutor - UMPG, SOLAR Music


Here's another quote from JLS:

The form of a bound is restricted (only the first element may be a class or type variable, and only one type variable may appear in the bound) to preclude certain awkward situations coming into existence.

What exactly are those awkward situations, I don't know.

Why can't I install Grand Theft Auto V?, If you can't download Grand Theft Auto V, make sure that your network is properly set up. If you're getting an Epic Games Launcher error co. Absolutely. Being overweight, getting too little exercise, and smoking all can work against the good blood flow that is key to erections. For some men, a little alcohol may help take the edge off


This probably does not answer the root question, but just want to point out that the spec unambiguously forbids it. Google search for the error message took me to this blog entry, which further points to jls 4.4:

The bound consists of either a type variable, or a class or interface type T possibly followed by further interface types I1 , ..., In.

So, if you use type parameter as bound you cannot use any other bound, just as the error message says.

Why the restriction? I have no idea.

Coronavirus: Why are international comparisons difficult?, So, does the virus need to be the main cause of death, or does any mention on a death certificate count? Are you really comparing like with like? Van Halen - Why Can't This Be Love (1986). Most popular single released from the album "5150" with new lead singer Sammy Hagar. Van Halen - Why Can't This Be Love Lyrics: Whoa, here it comes. That


Why Is My Period Late?, Worried about a late period, but know you aren't pregnant? Missed or late periods can happen for plenty of other reasons including stress, your form of birth​  Can’t go? You’re not alone. About 20% of Americans have occasional constipation-- bowel movements less than three times a week.Or if they do poop, the output is hard, small, and painful to


I Can't Stop Crying: Why We Cry and When to Seek Help, Do you cry too much? There are no guidelines for how much crying is too much. A study in the 1980s found that women cry an average of 5.3 times per  You can also run the Internet Connections troubleshooter by: 1. Clicking the Start button , and then clicking Control Panel. 2. In the search box, type troubleshooter, and then click Troubleshooting. 3. Under Network and Internet, click Connect to the Internet. For more troubleshooting help, see Why can’t I connect to the Internet?


Why Can't the Government Just Print More Money to Get Out of Debt?, Unless there is an increase in economic activity commensurate with the amount of money that is created, printing money to pay off the debt would make inflation  There could be several reasons why you aren't receiving your e-mail messages. Please review the following situations to see if one applies to you: Did you just move your domain name to Network Solutions® or register a new domain name? If so, it will take approximately 12-36 hours for the domain name information to propagate to servers worldwide.