9

As far as I understand, variable evaluation is done at run time. However, type evaluation is done at compile time in Java.

Also as I see, making a variable constant (I am using local variables but it changes nothing about the concept above), will make its value known at compile time.

I provide you two examples to test this concept. The first is working and the second is not.

Could someone explain to me why making the variable constant allows me to assign a short variable to an int variable, whereas I cannot assign an int variable to a long?

// Working example
final int x = 10;
short y = x;

// Non-working example
final long a = 10L;
int b = a;
Boann
  • 48,794
  • 16
  • 117
  • 146
Georgi Velev
  • 166
  • 10
  • 3
    you cannot make an int as a long. The opposite is true though. This has nothing to do with the `final`-declaration. `final` only means that you cannot change it, it is a constant – John Jun 12 '19 at 11:57
  • 2
    there is no implicit conversion from `long` to `int`. You need an explicit cast for the assignment. – Jack Jun 12 '19 at 11:57
  • @AdrienBrunelat if you gonna answer then please answer the question I asked. I know very well cannot . I can't assign an int to short as well, isn't it ? However making it final (1-st example) make it works ?! Why the 1-st is working and 2-nd not. Very simple question I ask . – Georgi Velev Jun 12 '19 at 11:58
  • @John you are correct only at a half. Your statement is correct in terms of reference variables but on primitives it also make it visible on compile time. So why I cannot assign the 10 to an int since compiler sees it is 10? – Georgi Velev Jun 12 '19 at 12:02
  • @GeorgiVelev I missed that part. I see the point of the question now. Interesting.You should phrase that in a less confusing manner though. – Adrien Brunelat Jun 12 '19 at 12:05
  • 1
    @GeorgiVelev I think I misread your question the first time, but Andy's answer clarifies it all I think. – John Jun 12 '19 at 12:13
  • 1
    Relevant: [Why is an implicit narrowing conversion allowed from int to byte but not from long to int?](https://stackoverflow.com/questions/48025164/why-is-an-implicit-narrowing-conversion-allowed-from-int-to-byte-but-not-from-lo) – TiiJ7 Jun 12 '19 at 12:16
  • `final` does not mean "known at compile time"; it means "assigned one time only"; you can perfectly assign user input at run time to a `final` variable. – Boann Jun 12 '19 at 14:19

1 Answers1

15

The relevant part of the language spec is JLS 5.2, Assignment Contexts:

In addition, if the expression is a constant expression (§15.28) of type byte, short, char, or int:

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

Making the a and x variables final makes them constant expressions (because they're initialized with constant values too).

The first example works because x is a constant int that you're trying to assign to a short variable, and the value is representable in a short; the second example doesn't because x is a constant long, and you're trying to assign it to an int variable (the value is representable, but this doesn't matter because it is already disqualified from implicit narrowing conversion).

Community
  • 1
  • 1
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • The first example works because it's final int to short **and** is between 32767 and -32768 (short value range). – Adrien Brunelat Jun 12 '19 at 12:07
  • I'm just surprised the same evaluation is not made for long. Why wouldn't it be possible to assign a long to and int if it's final and below Integer.MAX_VALUE? – Adrien Brunelat Jun 12 '19 at 12:08
  • I had no idea I cannot make long constant. Thanks @Andy Turner. – Georgi Velev Jun 12 '19 at 12:09
  • 3
    @GeorgiVelev "I had no idea I cannot make long constant" you can: it's still a [constant expression](https://docs.oracle.com/javase/specs/jls/se12/html/jls-15.html#jls-15.28). You just can't use it in this specific context. (As an example, if it weren't a constant expression, you wouldn't be able to use `s` as a case label [in this sample code](https://ideone.com/IOw4Yo)) – Andy Turner Jun 12 '19 at 12:10
  • Thanks one more time @AndyTurner. Yes, I am familiar that switch statement requires "compile-time known label values" , also a wonderful example on your side (many thanks !) I just didn't know the limitations according assignment. Thank you for your time on this topic Andy ! – Georgi Velev Jun 12 '19 at 12:17
  • 1
    Does this really answer the question? It explains that this is "by specification" and not a bug, but it does not really explain why it does not work for `long`, does it? – tobias_k Jun 12 '19 at 12:23
  • 2
    @tobias_k any answer to the *why* will be guesswork, unless provided by somebody involved in writing the specification. I'll stick to facts for the answer; my *guess* would be that this behavior primarily exists because of the lack of byte/short literals in the language, so it removes a minor bit of friction to assign a variable of that type; and, eh, why not extend that to all constant expressions, it's maybe easier to allow than forbid. But since there are both long and int literals, there was no original need to provide this for long to int. But like I say: mere conjecture. – Andy Turner Jun 12 '19 at 13:47
  • @tobias_k (I base my conjecture about byte/short literals on [the form this takes in version 1 of the spec](http://titanium.cs.berkeley.edu/doc/java-langspec-1.0.pdf#page=87)) – Andy Turner Jun 12 '19 at 13:49
  • 1
    @AndyTurner That's a very good point and likely the reason. I think you could add this to the answer, maybe with a short "disclaimer". Conversely, if it _were_ allowed, then `int b = 10L` would also be legal, which does not make much sense and rather points to a problem or typo. – tobias_k Jun 12 '19 at 13:59