11

I wonder why does this piece of code compile successfully?

Source code:

abstract class A<K extends Number>
{
    public abstract <M> A<? super M> useMe(A<? super M> k);
}

Compiled successfully

How does it work and why does this compile? M is any type, so why it can be used?. Should it be: <M extends Number>? This will not compile:

abstract class A<K extends Number>
{
    public abstract <M> A<? super M> useMe(A<M> k);
}

Error message:

type argument M is not within bounds of type variable K where M, K are type variables: M extends Object declared in method useMe(A) K extends Number declared in class A

What is the difference?

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
Pawel
  • 1,457
  • 1
  • 11
  • 22
  • You might get better answers by including the actual error message you receive from the compiler. (We shouldn't have to start an IDE to understand your question.) – meriton Dec 23 '13 at 23:47
  • What actual error message? The question is why DOES this compile? – Dawood ibn Kareem Dec 23 '13 at 23:47
  • Note for those who are curious, replacing `` with `` in the later allows it to compile as well. – Guvante Dec 23 '13 at 23:49
  • It seems like the return types can be replaced with void for the sake of the question. – Paul Bellora Dec 23 '13 at 23:49
  • I guess the only real answer to this question is "because the compiler isn't smart enough to work out that it shouldn't allow this". – Dawood ibn Kareem Dec 23 '13 at 23:49
  • @DavidWallace: He is surprised that the error message he gets from the second code sample (bound mismatch on the type parameter of A) doesn't appear for the first code sample as well, since M isn't known to be a subtype of Number in either case. – meriton Dec 23 '13 at 23:50
  • @DavidWallace: I think it is deeper than that, by calling it a supertype, you cannot possibly define it in a way that works, since ` super M>` always includes `object` which can't satisfy the requirement anyway. – Guvante Dec 23 '13 at 23:55
  • My answer would be: Your second declaration will not compile because in function parameter `A k`: the bound of types of `M` is actually strictly `Object` in compilie time but as your class `A` is declared `A` can only be parametrized with type with which upper bound `Number`. However, the first compiles, because using `? super M` at lease convince the compiler that the type might have a bound other than the `Object` and can be found out in runtime. – Sage Dec 23 '13 at 23:57
  • @Guvante If you and I can look at those few lines of code and say "hey, this shouldn't compile", then the compiler ought to be able to too. – Dawood ibn Kareem Dec 23 '13 at 23:57
  • I added compiler meessage. So it is a bug? – Pawel Dec 23 '13 at 23:58
  • Pawel: You would have to read the spec to determine what guarantees it makes in this situation. They may have included a note about this behaviour indirectly. @DavidWallace: The spec is as complete as it can be, and the call site can finalize the contract, so whether it compiles or not is decided by the specifics of how the language is defined. – Guvante Dec 23 '13 at 23:59

4 Answers4

5

This compiler behavior was discussed on this Eclipse bug. Originally, the Eclipse compiler did error for the expression in your example, while javac did not. Although I haven't yet searched the JLS directly, the consensus seems to be that there is nothing in the spec requiring lower bounded wildcards to be checked against type parameter bounds. In this situation it's ultimately left to the caller to assign a type that satisfies the constraints (as surmised by Stephan Herrmann on that post).

Paul Bellora
  • 54,340
  • 18
  • 130
  • 181
  • are you sure, that javac won't! My NetBeans also keeps reporting this exception. – Sage Dec 24 '13 at 00:16
  • @Sage It compiles for me in Eclipse (1.7) and javac (sun-jdk 1.7) Ideone: http://ideone.com/lRb23T. What compiler is your Netbeans set to? – Paul Bellora Dec 24 '13 at 00:19
  • @Sage I have NetBeans 7.4 and first example compiles for me too. JDK 1.7. – Pawel Dec 24 '13 at 00:19
  • @Pawel, yes but the question is under the second example and difference with the first with mark `*why?*` :) ;) – Sage Dec 24 '13 at 00:21
  • 1
    @Sage Actually the question is why the first example does compile. – Paul Bellora Dec 24 '13 at 00:23
  • @PaulBellora, yes you are correct too. Actually i see the question as a query: the reason why first one compiles and second one doesn't, from both sides and that is what i wanted to mean. I think javaC is working as it stated the behavior of erasure: As i mentioned in the comment of OP's question panel: if parameter `T` is unbounded, the Java compiler replaces it with `Object`. Hence it exceeds the bound of `Number` which is the upper bound of parameter type of `A` I can be wrong though :) – Sage Dec 24 '13 at 00:43
  • @Sage I would have to disagree with that theory, since erasure only happens after all compile time checks. I think this is just a case of the JLS being practical by not enforcing overly complicated checks on types to whom assignments will be checked anyway. – Paul Bellora Dec 24 '13 at 00:59
  • I can understand them, why lower bound can be left and programmer should care about it. But upper bound can satisfies the requirements as lower bound too. – Pawel Dec 24 '13 at 21:25
2

This is a surprisingly meaningless piece of code.

All it is saying is that the class A takes a generic type K that is a Number and there is a method useMe that returns an A<T> with some pointless extra restriction on T (other than being a Number obviously).

Here's an implementation to show how little is being said by the sugar:

abstract class A<K extends Number> {
    public abstract <M> A<? super M> useMe(A<? super M> k);
}

class B extends A<Number> {

    @Override
    public <M> A<? super M> useMe(A<? super M> k) {
        // Not much more you can do here but this.
        return k;
    }

}

The ? super M stuff is just meaningless gobbledegook - all the compiler can derive from it is that both the parameter passed to it and the result returned must be a superclass of a specific unnamed class.

Generics are there to make detection of coding mistakes easy at compile time. Using mumbo-jumbo such as this is just misleading obfuscation.

OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
1

Adding <M extends Number> to the first example doesn't add anything the compiler cares about. Remember you are saying "a type which is a supertype of M" if we are saying "M is a subtype of Number" and "the type is a supertype of M", we aren't actually saying if the type is a subtype of Number.

For a better example, have M be Integer and the variable be of type A<Object>. While obviously that won't work, it satisfies all the requirements of the function correctly.

Since there exists no way to fix the function definition, it just lets it pass and assumes the call site will catch the problem.

Guvante
  • 18,775
  • 1
  • 33
  • 64
  • Exactly, both are different, so why this compiles? I know the meaning of symbols. – Pawel Dec 23 '13 at 23:38
  • @SotiriosDelimanolis: Sorry you are right, my first answer wasn't complete, thought about it some more and came up with a better one. – Guvante Dec 23 '13 at 23:57
-1

There are two parts to your question:

Part 1: What is the <M>?

The presence of a generic parameter on a method makes it a "typed method", which means the method has a generic type that is determined by the caller, usually by inference. It may be bounded. If the class has a type too and the method is an instance method, the two types are unrelated.

Part 2:

The generic types must match exactly. The reason boils down to the fact that if B is a subtype of A, SomeClass<T extends B> is not a subtype of SomeClass<T extends A>, more particularly, SomeClass<A> is not a subtype of SomeClass<? super A>.

Bohemian
  • 412,405
  • 93
  • 575
  • 722