4

To avoid confusion, I'm looking for the rule/JLS entry.

  • I'm not asking why Double -> int would fail, I'm asking about the way it fails

  • I'm aware of the lossy conversion, as I've mentioned in my question - I'm not asking about data loss between double -> int

  • I'm not asking for someone's "best guess" on why the developers designed it this way

I'm asking why Integer -> double performs a conversion (unbox & widening), while Double -> int performs no conversion (not even unboxing)

I'm looking for the JLS entry that mentions this Reference -> primitive conversion inconsistency, where unboxing occurs in one situation, but not the other.


This Integer -> double conversion compiles with no error

double d = Integer.valueOf(1);

It implies the following occurs:

  1. Integer is unboxed to int
  2. the int value undergoes a widening primitive conversion from int -> double

Integer is unboxed. The unboxed value is then widened. This gives the same behavior as int -> double


The creates the assumpsion that Double -> int will also unbox, giving the same behavior as double -> int

For the code

int i = Double.valueOf(1);

I would expect the error message

lossy conversion from double to int

Assuming the Double gets unboxed, we should observe the same behavior as double -> int

Instead, we get a typing error

Cannot convert Double to int


What is the explanation behind this behavior?

Why does unboxing occur between Integer -> double, but no unboxing occurs between Double -> int?

Why are these congruent:

  • Integer -> double
  • int -> double

But these aren't:

  • Double -> int
  • double -> int
8
  • 3
    Because it is easier to "upgrade" an integer to a float. If you go the other way, you sacrifice precision. As you described, this is a lossy conversion. Commented Oct 13, 2023 at 18:42
  • @Mr.Polywhirl So why wouldn't we get a lossy conversion message, as expected? Why is it a type error? Why does unboxing occur with Integer -> double, but doesn't seem to occur with Double -> int? That's the question I'm asking Commented Oct 13, 2023 at 18:48
  • 2
    Why should it try unboxing, if it knows it'll still get a type error at the other end? Commented Oct 13, 2023 at 18:51
  • double-to-int and Double-to-int are lossy conversions. Originally there was just a double-to-int giving C++ like warning. But nowadays a simple (int) cast is the expected style. Hence for Double-to-int no effort was made to just allow this unclear and indirect two-step conversion. Of course I was not present at the language design, but one would need to do implicitly (int)(double). Commented Oct 13, 2023 at 18:58
  • "Why did they design it this way"-questions are opinion based, and off-topic Commented Oct 13, 2023 at 19:25

4 Answers 4

2

Double only has two valueOf methods: one for String input and one for double inputs.

So, while you wrote Double.valueOf(1) where that 1 is clearly an int, the method signature is valueOf(double) so (if this were to even run) your int value would get upgraded to a double and then the method runs. As such, your int i = Double.valueOf(1) is really int i = Double.valueOf(1.0) to which the Java compiler goes "a double can't be safely converted to an int, so: no".

Although of course, it doesn't even need to do that: it knows the return value for Double.valueOf is a Double, no matter what you've put in, so it sees code that tries to assign a Double to an int, knows this is impossible, and without caring about anything else, it'll go "RHS is incompatible with LHS, so: no".

Though interestingly, "The[sic] creates the assumpsion[sic] that Double -> int will also unbox" is both wrong (no value unboxing happens) and not entirely wrong (there's type "unboxing"). You're giving Java an assignment with types int = Double, Java sees incompatible types, but also knows that this is a primitive assignment and that Double can unbox to the primitive double. However, as that's still an incompatible assignment, Java nopes out with the original int/Double incompatibility error (so that you know "what's wrong with your code" rather than "what's wrong in whatever code magic the parser applied")

Sign up to request clarification or add additional context in comments.

5 Comments

Some valid points, +1.
I wasn't assuming Double.valueOf(1) accepted an int. I could have written Double.valueOf(...). You keep talking as if you were the compiler or were looking through the eyes of the compiler , such as "Java sees incompatible types" and "RHS is incompatible with LHS, so: no", but I'm not looking for someone's perspective on it, rather a snippit from the devs themselves that explain the behavior. Do you have a reference from the JLS that explains the behavior?
The docs. E.g. docs.oracle.com/javase/specs/jls/se7/html/jls-5.html and all the other ones explaining in more detail than anyone would ever want exactly how the compiler and JVM work. Java is extremely exhaustively documented.
@Mike'Pomax'Kamermans I know, I was asking for the section that covered this rule
and lo and behold, reading the documentation made you find it. You're welcome.
0

"... This gives the same behavior as int -> double ..."

That's what the conversion is.  The Integer object is un-boxed to an int.
There is never an Integer to double.

"... Assuming the Double gets unboxed, we should observe the same behavior as double -> int ..."

I don't expect that from Java at all.
And, unfortunately, I don't have the metrics.

I imagine if the value can't un-box and assign in a single operation, the compiler would fail.

Comments

0

To avoid confusion, I'm looking for the rule/JLS entry.

  • I'm not asking why Double -> int would fail, I'm asking about the way it fails

You're asking for something which doesn't exist.

The Java Language Specification does not generally mandate the wording of compilation errors. In particular, the relevant section of the Java Language Specification merely describes which implicit conversions are permitted in an assignment context, but does not enumerate the combinations of conversions that aren't permitted, let alone which error message should be given in each case (or how to choose the message if several cases apply).

All the specification mandates is that the compiler rejects invalid conversions, how it words the message is up to the compiler implementors, and different compilers actually emit different messages.

For instance, if you ask the Eclipse Compiler for Java to compile

int i = Math.PI;
int j = Double.valueOf(Math.PI);

it says:

Type mismatch: cannot convert from double to int
Type mismatch: cannot convert from Double to int

4 Comments

"It doesnt exist", that's not true. It's under conversion context, someone on Discord helped me find it. docs.oracle.com/javase/specs/jls/se21/html/jls-5.html#jls-5.5
As your link puts it: "Casting contexts allow the operand of a cast expression (§15.16) to be converted to the type explicitly named by the cast operator. Compared to assignment contexts and invocation contexts, casting contexts allow the use of more of the conversions defined in §5.1, and allow more combinations of those conversions."
That is, that table describes how code using an explicit type cast is type checked. Your code doesn't use a cast operator, and therefore that table does not apply.
I understand the confusion with "casting expression", but if that were the case, Object obj = 5; would require an explicit cast if the table only included explicit casts. It also includes implicit casts. It defines the rule for the conversion, explicit or implicit
0

There's a table showing allowed conversions within §5.5:

  • signifies no conversion allowed
  • ω signifies widening primitive conversion
  • ⊗ signifies unboxing conversion

enter image description here

The table shows Integer -> double performs unboxing & widening conversions, but Double -> int does not perform unboxing.

Since unboxing doesn't occur, we get a type error rather than a lossy conversion error.

2 Comments

That table shows allowed conversions in a "casting context". For instance, this table shows you that (int) Double.valueOf(1) does not compile. But your code doesn't use a cast operator, but a plain assignment, i.e. int i = Double.valueOf(1), for which different rules apply.
@meriton it doesn't need a cast operator, cause implicit casting exists

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.