Why can I assign an integer literal to a short type variable but not to a short type method parameter?
Asked Answered
A

2

18

Why can I do this:

short a = 5;

But not this:

void setNum(short a);

setNum(5);

It throws:

Possible lossy conversion from int to short

I understand that 5 is an integer literal and you have to cast it. I also understand that if the value is not a constant then is obvious that it needs to throw that error because maybe the value reaches the limit of a short type. But why if the compiler knows I'm passing a constant that a short can hold (as in the assignment) it doesn't let it compile? I mean, what is the difference between them?

Arthritis answered 6/10, 2016 at 1:57 Comment(8)
The compiler follows the language spec, which apparently does not call for the conversion in this case. That does seem a bit inconsistent, but there is probably not a good answer to why the spec is like this (unless someone digs up something from a Java language spec mailing list).Kissable
This is just a guess, but: In the first example, maybe 5 can be read as a short literal when parsing, whereas in the second example the parser doesn't know the type of the method argument, so it defaults to being parsed as an int literal.Henbane
@ChrisMartin Yeah, what an inconsistent parser then, as Thilo saidArthritis
But you can't know the expected type at parse time in the second case. Consider a cyclic dependency between files. It does the best that can be done.Henbane
My guess is that when you declare a variable like short x = 1;, the JLS will let you avoid some casts even though the literal value is an int. However in all other situations, such as calling a method, there is no syntactical abbreviation allowed.Cohbert
Another example is that you can declare an array as int[] x = {1,2,3};, but if you create a new array as a value then you must be explicit, like foo(new int[]{1,2,3});Cohbert
Deleted my old comment and adding another one, that's because the Java Language Specification is designed in such a way that the expressions on the right side are always evaluated as int by default. As for why, I'd suggest go through the below link. https://mcmap.net/q/262363/-primitive-type-39-short-39-casting-in-javaFallingout
This is at least close to being a duplicate of #478250 (and others)Diplomatics
V
11

In order to understand why the assignment type-conversion works whilst the invocation one is rejected, one has to refer to the Java Language Specification topic for both narrowing primitive conversions and the context of that conversion: assignment context and invocation context.

According to the JLS, the narrowing primitive conversion is allowed in assignment context if:

A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.

... which matches your case with int constant 5 when assigned to short a.

No such narrowing primitive conversion is allowed in the invocation context, which explains why your call to setNum(short) fails when passed the int constant 5.

But why if the compiler knows I'm passing a constant that a short can hold (as in the assignment) it doesn't let it compile? I mean, what is the difference between them?

The JLS must not have wanted to burden compilers with this additional logic. In the invocation case, the argument which is type-matched to the formal parameter is an expression - the compiler already has to determine the type, but it shouldn't need to also check if the expression's value can also safely be narrowed. In this case, being a constant, it is clear to us that it can be, but in execution context the compiler is allowed to not bother with that check, and is in-fact correct to disallow it.

It should be fairly clear that when expressions are allowed, it would be much easier for bugs to creep in where narrowing cannot be done without losing precision, so the JLS and compilers just disallow it in all cases.

The same is true in numeric context, so the declaration:

short a = 5;short b = a * 5;

... is similarly not allowed, despite being clearly comprised of constants which narrow correctly.

Viki answered 6/10, 2016 at 4:9 Comment(1)
"The JLS must not have wanted to burden compilers with this additional logic." This is probably the correct reason, but seems like a poor decision by the JLS. We live in a day where the more a compiler can do to make life simpler for the developer, the better. Passing a literal value to an method's parameter that is a short is a very common use-case, so the burden on the compiler seems warranted. Considering how often the Java compiler infers types (and even generic types) in other situations, this seems like a sore spot. A shame that they still haven't fixed it in the language spec.Arboriculture
F
0

When you type 5, this is automatically an integer. I'm not sure what IDE you are using that's giving you the error, but the reason it's warning you is because you're converting a larger storage capacity value to a smaller one, which although not in your case, could result in you losing data. This is called a narrowing conversion.

Integers can hold 32 bits of data, while shorts can only hold 16 bits of data. So, for example (in reality the numbers would be much bigger), an int had a value equal to 50, and you then cast it to a short, the data would be cut to "5" because a short doesn't have a large enough memory allocation.

That code you posted won't work because when you define the short like follows:

short a = 5;

You're directly creating a short, and the number is small enough that the short can hold it. When you type "5" alone as a method argument, it's handled as an integer, and the JVM doesn't know that it's small and safe to make a short. To make the "5" suitable as an argument for the method, you need to turn it into a short using a narrowing conversion, as follows:

setNum((short) 5);

But as stated, if you don't actually know the value of the int, and you're not sure it's small enough to be turned into a short, this can create errors in your code as some of the number will be chopped off.

(Here is some Oracle documentation on this)

Franci answered 6/10, 2016 at 2:4 Comment(3)
That does not explain while the implicit (and safe) conversion is allowed in the first example but not in the second.Kissable
Actually your example with 50 being cut down to 5 is wrong - if anything it would be 2 (50 = 0x32 if I counted correctly) if stored in a 4-bit integerAshburn
@To Vineyard yeah, it was intended to be a super basic example to get the point acrossFranci

© 2022 - 2024 — McMap. All rights reserved.