16

JLS §5.2 of Java SE 11 contains some new type conversion cases which JLS of Java 8 doesn't have, see item 4 and item 5 in the list:

Assignment contexts allow the use of one of the following:

  1. an identity conversion
  2. a widening primitive conversion
  3. a widening reference conversion
  4. a widening reference conversion followed by an unboxing conversion
  5. a widening reference conversion followed by an unboxing conversion, then followed by a widening primitive conversion
  6. a boxing conversion
  7. a boxing conversion followed by a widening reference conversion
  8. an unboxing conversion
  9. an unboxing conversion followed by a widening primitive conversion

I don't understand the case 4 and case 5 in the list. Could anyone give me some explanation with examples? If possible, please also explain its practical usage.


Update:

As @Naman commented, here is the proposal to change the JLS - JDK-8166326 : 5.2: Allow widening before unboxing which was in effect since Java-9. In the report, it mentioned:

This behavior is especially important for interoperability with capture: various existing programs expect to be able to treat the elements of a List<? extends Integer> as if they were ints.

List<? extends Integer> li = null;
int i = li.get(0);

This may imply that this JLS change do have a practical necessary. But I still don't understand why <? extends Integer> is important. What does interoperability with capture mean and why is it important? What do these various existing programs look like? Are they java code (I know some other languages also work on JVM and may have interaction with Java code)?

Frank Mi
  • 452
  • 3
  • 11
  • 1
    Given the fact that all 8 classes eligible for unboxing conversions are `final`, I do not see the point of those two cases either. Those phrases, however, are repeated in [§ 5.3](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html#jls-5.3), [§ 5.5](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html#jls-5.5) and partially [§ 5.6](https://docs.oracle.com/javase/specs/jls/se11/html/jls-5.html#jls-5.3). – Turing85 Aug 30 '20 at 23:15
  • @Turing85 could you elaborate on why `final` makes importance here? – Giorgi Tsiklauri Aug 30 '20 at 23:23
  • 1
    @GiorgiTsiklauri If one could `public class MyBoolean extends Boolean { ... }`, for example, it would be logical that `boolean bool = myBoolean;` works. But since we cannot extend from the wrappers, I do not understand how we can get into a situation where we need to first widen the reference (from something that is a subtype of a wrapper) and then unbox. – Turing85 Aug 30 '20 at 23:28
  • 1
    @GiorgiTsiklauri the only possibility that comes to my mind would be to use a tool like PowerMock to mock (one of) the wrappers... – Turing85 Aug 30 '20 at 23:31
  • Makes sense. *Subtype* stops the show here; otherwise, `Integer bigNumber = 10000000; Byte littleNumber = 5; long l = (bigNumber=littleNumber+2);` would demonstrate both - widening followed by unboxing and widening followed by unboxing followed by a widening primitive conversion. However, this is OK for JDK 8 alike. Just tested. – Giorgi Tsiklauri Aug 30 '20 at 23:33
  • @Turing85 I don't think the *Mock* is in the picture for JLS. – Giorgi Tsiklauri Aug 30 '20 at 23:35
  • @GiorgiTsiklauri I did not say that this is why it is in there. I only said that this is the only way I can think of how one could extend from one of the final wrapper classes :) – Turing85 Aug 31 '20 at 00:33
  • 3
    Here is the proposal to change the JLS - [JDK-8166326 : 5.2: Allow widening before unboxing](https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8166326) which was in effect since Java-9. – Naman Aug 31 '20 at 03:32
  • 1
    @Naman ok, so now we need to come up with a reasonable usecase for the examples mentioned in the change request - being able to treat a `` as `int`s. – Hulk Aug 31 '20 at 04:27
  • 2
    @Hulk given that the compilers did already behave that way, there is no need to find practical use cases for that. Ensuring that specification and actual compiler behavior are in line, is already enough motivation for such a change. – Holger Aug 31 '20 at 07:53
  • @Holger Agreed. – Hulk Aug 31 '20 at 08:39
  • The spec mentions closures which implies that the `` (or more precisely ` extends Integer>`) could easily be *infered* and not explicitly written by the developer, which can quite easily be constructed. – Joachim Sauer Aug 31 '20 at 14:36
  • @JoachimSauer Could you elaborate it? Though I 've read through the discussion here, I'm still stuck at the point that wrapper classes are final, so that no class can extend them. – Frank Mi Aug 31 '20 at 14:49
  • 1
    @FrankMi: what I meant is that *writing* `` is obviously non-sensical (since the wrappers are final), but it's not in itself an invalid type signature. And type inferrence (which often happens when closures are applied) can often lead to types that look like that (you don't tend to *see* those type definitions, which is the whole point of type inference). Now if something was inferred to be of the type `T extends Integer` then it would be nice if you could use it as if it was just an `int`. This rule simply allows that. – Joachim Sauer Aug 31 '20 at 14:56
  • @JoachimSauer Thanks a lot for your explanation, though I am still confused with the type inference part. The problem is that I can't imagine what can be inferred to `T extends Integer` without an example. – Frank Mi Aug 31 '20 at 15:17
  • @Holger Thank you for your reply. Could you elaborate on why "there is no need to find practical use cases"? Do you mean there's no way to have a real practical code to work that way? If the compiler works a certain way, it should have already considered that a certain case could happen in real code, isn't it? – Frank Mi Aug 31 '20 at 15:21
  • 1
    The bug report linked in [this comment](https://stackoverflow.com/questions/63662871/java-se-11-new-cases-of-type-conversion-in-java-language-specification?noredirect=1#comment112579052_63662871) does already contain examples. And it doesn’t matter whether such constructs will happen in real applications, as the compiler follows formal rules that are designed to tell for *every* case whether it is legal code or not. – Holger Aug 31 '20 at 15:34
  • @Holger Though the example in that link doesn't make much sense, the bug report also mentioned that: This behavior is especially important for **interoperability with capture**: **various existing programs** expect to be able to treat the elements of a `List extends Integer>` as if they were ints. So the change really took practical cases into account. But what is **interoperability with capture**? And what are the **various existing code** look like? Are they java code? – Frank Mi Aug 31 '20 at 15:57

1 Answers1

1

TL;DR

…The problem with boxing is that it is [ad-hoc] and expensive; extensive work has gone on under the hood to address both of these concerns“ — Brian Goetz, State of Valhalla, March 2020

That makes me suspect that those changes to the spec you refer to are preparing the language for the planned „Codes like a class, works like an int“ capabilities of L-World discussed in the above link.


…Could anyone give me some explanation with examples?…

The example in the 2016 bug report linked to in the comments (plus the 2013 one that one links to) gives the best example of #4 in your list. Based on those, here's an example of #5 in your list…

< I extends Integer > void soAns0( I intRef ) {

   int intPrim;

   long longPrim = /* (3) a widening primitive conversion */
   intPrim =       /* (2) an unboxing conversion */
   intRef;         /* (1) a widening reference */
}

…If possible, please also explain its practical usage…

The natural numbers are often referred to as „integers“. The Java Language Tutorial shows one practical usage of restricting the natural numbers to only be of type Integer

public class NaturalNumber<T extends Integer> {

    private T n;

    public NaturalNumber(T n)  { this.n = n; }

    public boolean isEven() {
        return n.intValue() % 2 == 0;
    }

    // …
}

There's also this example in the API documentation for RowFilter<M,​I>

…
  public boolean include(Entry<? extends PersonModel, ? extends Integer> entry) {
    …
    Person person = personModel.getPerson(entry.getIdentifier());
    …
  }
…

Another use case where T extends Integer might be appropriate is if you want to explicitly forbid your class from being parameterized with any other type of Number AND explicitly forbid adding Integers into that parameterized type — by leveraging the Get/Put Principle.

Say you have some code that, given some number, will create that number of objects. If you want to forbid bad actors from overwhelming your system by bombarding it with Long.MAX_VALUE number of DOS attempts, bounding <T extends Short> could be one way to limit the number of objects that would get created in any one method call.

…What do these various existing programs look like? Are they java code?…

A search on github for T extends Integer turns up eighty-thousand some hits.

…What does interoperability with capture mean?…

I defer the definitive explanation of capture conversion to the JLS itself.

…and why is it important?…

Capture conversion is important because of it's connection to type inference.

The notation ‹Integer <: α› in the JLS' section on type inference is more or less a formal way of expressing Integer extends Integer (what T extends Integer effectively means)…

From Arrays.asList(1, 2.0), we have the constraint formulas ‹1 → α› and ‹2.0 → > α›. Through reduction, these will become the constraint formulas ‹int → α› and ‹double → α›, and then ‹Integer <: α› and ‹Double <: α›.

Given that, it's not surprising that the JDK uses <T extends Integer> a lot for Generics/Type Inference related tests of the JDK itself.

Here's my absolute favorite use of <T extends Integer> in the wild.

Mostly though, I would bet those unboxing-/widening-related changes to the spec have more than a little to do with Valhalla.

deduper
  • 1,944
  • 9
  • 22
  • Thanks for your answer! Please give me some time to understand it. – Frank Mi Sep 22 '20 at 19:58
  • „*…Please give me some time to understand it…*“ – @FrankMi — You're more than welcome. What, in particular, did you find not easy to understand? I'm more than happy to paraphrase anything to be more straightforward/less verbose. TIA. – deduper Sep 22 '20 at 20:14