Java SE 11 - New Cases of Type Conversion in Java Language Specification
Asked Answered
F

1

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)?

Feleciafeledy answered 30/8, 2020 at 22:53 Comment(18)
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, § 5.5 and partially § 5.6.Disciplinant
@Disciplinant could you elaborate on why final makes importance here?Mho
@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.Disciplinant
@GiorgiTsiklauri the only possibility that comes to my mind would be to use a tool like PowerMock to mock (one of) the wrappers...Disciplinant
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.Mho
@Disciplinant I don't think the Mock is in the picture for JLS.Mho
@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 :)Disciplinant
Here is the proposal to change the JLS - JDK-8166326 : 5.2: Allow widening before unboxing which was in effect since Java-9.Halcomb
@Halcomb 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 <T extends Integer> as ints.Prut
@Prut 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.Procreant
@Procreant Agreed.Prut
The spec mentions closures which implies that the <T extends Integer> (or more precisely <? extends Integer>) could easily be infered and not explicitly written by the developer, which can quite easily be constructed.Sanity
@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.Feleciafeledy
@FrankMi: what I meant is that writing <T extends Integer> 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.Sanity
@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.Feleciafeledy
@Procreant 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?Feleciafeledy
The bug report linked in this comment 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.Procreant
@Procreant 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?Feleciafeledy
S
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.

Suborn answered 18/9, 2020 at 17:56 Comment(2)
Thanks for your answer! Please give me some time to understand it.Feleciafeledy
…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.Suborn

© 2022 - 2024 — McMap. All rights reserved.