Unboxing conversion of generic types with box as a bound
Kevin Bourrillion
kevin.bourrillion at oracle.com
Tue Dec 16 15:37:23 UTC 2025
Interesting. That also represents an unexpected inconsistency between x++ and x+=1.
Confidential- Oracle Internal
From: Maurizio Cimadamore <maurizio.cimadamore at oracle.com>
Date: Tuesday, December 16, 2025 at 2:07 AM
To: Tagir Valeev <amaembo at gmail.com>, Kevin Bourrillion <kevin.bourrillion at oracle.com>
Cc: compiler-dev at openjdk.org <compiler-dev at openjdk.org>
Subject: Re: Unboxing conversion of generic types with box as a bound
Hi Tagir,
I agree with all Kevin said. I'm also with you re. things being inconsistent.
The reason as to why javac allows what it allows is that it implements a more lax version of "unboxing" (mostly because of old issues that were "fixed" in Java 6, see end of this email). E.g. anything whose supertype is a boxed type can be unboxed, and so type variables get caught in the mix.
But since String doesn't use unboxing conversion, there's no special behavior there.
There's also other oddities and anomalies -- for instance:
```
<X extends Integer> void m(Integer i, X x) {
x++; // ok
x = (Integer)x; // error
}
```
The first statement being ok means that basically we know that X can only be instantiated with Integer (as Integer is final). But then why does the cast fail?
I think more work needs to be done on both sides (spec _and_ compiler) to flesh out this story in full.
Maurizio
[1] - https://bugs.openjdk.org/browse/JDK-5043021
[2] - https://bugs.openjdk.org/browse/JDK-6240565
On 16/12/2025 08:04, Tagir Valeev wrote:
Hello, Kevin!
Indeed, this is helpful, thank you. It looks like, we should support this case in IntelliJ IDEA as well. Here's another sample that might be related:
private static <X extends String> void concat(X x) {
System.out.println(x+1);
}
void main() {
concat("hello");
}
This code is not compilable, we have a compilation error:
java: bad operand types for binary operator '+'
first type: X
second type: int
It looks inconsistent to me that 'X extends Integer' is being treated as Integer, but 'X extends String' is not being treated as String. But probably it's a different case, as unboxing is not involved here.
With best regards,
Tagir Valeev.
On Tue, Dec 16, 2025 at 12:01 AM Kevin Bourrillion <kevin.bourrillion at oracle.com<mailto:kevin.bourrillion at oracle.com>> wrote:
Hey Tagir, we were discussing this internally a couple months ago, and I felt there was a general consensus that this is indeed a spec bug. Javac's permissiveness seems totally reasonable here.
We either need to insert more WRCs before unbox conversions (like the one you found), or (a seemingly-simpler approach that I hope will work) just tweak the specification of unboxing conversion itself so it applies to any subtype of Integer-etc., instead of only the exact type. (Of course this also affects intersection types and capture types as well as the case in your example.)
We didn't jump on fixing it yet but it's slated to happen sooner or later as part of a series of type-conversion cleanups we're working on. Hopefully just getting this answer unblocks you for now?
Confidential- Oracle Internal
From: compiler-dev <compiler-dev-retn at openjdk.org<mailto:compiler-dev-retn at openjdk.org>> on behalf of Tagir Valeev <amaembo at gmail.com<mailto:amaembo at gmail.com>>
Date: Monday, December 15, 2025 at 9:21 AM
To: compiler-dev at openjdk.org<mailto:compiler-dev at openjdk.org> <compiler-dev at openjdk.org<mailto:compiler-dev at openjdk.org>>
Subject: Unboxing conversion of generic types with box as a bound
Hello!
I noticed that the following program is compilable without errors using OpenJDK 25 javac:
private static <X extends Integer> void add(X x) {
System.out.println(+x);
}
void main() {
add(10);
}
I wonder if it's correct. The JLS 15.15.3 says [1]:
The type of the operand expression of the unary + operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs.
The JLS 5.1.8, in turn, says [2]:
Unboxing conversion treats expressions of a reference type as expressions of a corresponding primitive type. Specifically, the following eight conversions are called the unboxing conversions:
...
From type Integer to type int
...
At run time, unboxing conversion proceeds as follows:
...
If r is a reference of type Integer, then unboxing conversion converts r into r.intValue()
In my case, the type of reference is X, rather than Integer. So my question, should I read 'type Integer' here as 'type Integer, or any generic type whose upper bound is type Integer'? In other words, should we assume that the X type declared as `X extends Integer` is convertible to a primitive numeric type? It looks like, here a widening reference conversion (5.1.5) happens before an unboxing conversion, but 15.15.3 does not say about widening reference conversion. I've also checked 5.6 "Numeric contexts", but it also does not mention that before unboxing conversion, a widening reference conversion might occur.
Could anybody please clarify whether the compiler is wrong or I read the specification incorrectly? Thank you in advance!
With best regards,
Tagir Valeev
[1] https://docs.oracle.com/javase/specs/jls/se25/html/jls-15.html#jls-15.15.3
[2] https://docs.oracle.com/javase/specs/jls/se25/html/jls-5.html#jls-5.1.8
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/compiler-dev/attachments/20251216/c91b1798/attachment-0001.htm>
More information about the compiler-dev
mailing list