46

I understand why the following is wrong:

byte a = 3; 
byte b = 8; 
byte c = a + b;  // compile error

It won't compile. Expressions always result in an int. So we should have done the explicit cast:

byte c = (byte) (a + b);   // valid code

But I don't understand why the following is correct:

byte d = 3 + 8;   // it's valid! why?

Because literal integer (such as 3 or 8) is always implicitly an int. And int-or-smaller expressions always result in an int too. Can anybody explain what is going on here?

The only thing I can guess is that the compiler equates this expression to the following:

byte d = 11;

and doesn't consider this an expression.

Smi
  • 12,505
  • 9
  • 53
  • 61
Alex
  • 1,087
  • 1
  • 8
  • 16
  • 1
    Given the registers are 32-bit or 64-bit, I would wonder why you are using `byte` in the first place. – Peter Lawrey Jun 14 '13 at 14:44
  • `byte c = a + b;`, because it doesn't fit in byte. – Rong Nguyen Jun 14 '13 at 14:44
  • 6
    @RongNK: No, that's wrong. It's because `a + b` evaluates to an `int`, and the compiler doesn't permit implicit narrowing conversions of non-constant expressions. – jason Jun 14 '13 at 15:31

3 Answers3

47

This has less† to do with whether or not 3 + 8 is evaluated to 11 at compile-time, and more to do with the fact the compiler is explicitly permitted to implicitly narrow ints to bytes in certain cases. In particular, the language specification explicitly permits implicit narrowing conversions to byte of constant expressions of type int that can fit in a byte at compile-time.

The relevant section of the JLS here is section §5.2:

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 type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.

The compile-time narrowing of constants means that code such as: byte theAnswer = 42; is allowed. Without the narrowing, the fact that the integer literal 42 has type int would mean that a cast to byte would be required:

†: Obviously, as per the specification, the constant expression needs to be evaluated to see if it fits in the narrower type or not. But the salient point is that without this section of the specification, the compiler would not be permitted to make the implicit narrowing conversion.

Let's be clear here:

byte a = 3; 
byte b = 8; 

The reason that these are permitted is because of the above section of the specification. That is, the compiler is allowed to make the implicit narrowing conversion of the literal 3 to a byte. It's not because the compiler evaluates the constant expression 3 to its value 3 at compile-time.

jason
  • 220,745
  • 31
  • 400
  • 507
  • @Paul Bellora: I'm sorry you feel that way; I'm not intending to be combative. Please help me understand how I can come across not that way? – jason Jun 14 '13 at 14:52
  • 2
    While your answer is super-helpful, it seems specifically designed to take down the other existing answer. Ideally, they would've simply supported one another, one with examples, the other with JLS citations. I try to keep my downvotes constructive, but in this case I just found the tone very distracting, so I felt like no vote with a comment was a good compromise. – Paul Bellora Jun 14 '13 at 14:55
  • @Paul Bellora: No, that is not my intention at all, genuinely. :-( – jason Jun 14 '13 at 14:56
  • Okay, maybe I read too much into it then. Thanks for the great explanation. – Paul Bellora Jun 14 '13 at 15:02
  • +1. This must be why the compiler, instead of just compiling the code, is also doing some calculations. – Bhesh Gurung Jun 14 '13 at 15:02
  • Well, the compiler being able to calculate the exact value is *one of the preconditions* to allowing the conversion. So I'd say it has *a lot* to do with this. – Joachim Sauer Jun 15 '13 at 08:48
41

The only thing I can guess is that the compiler equates this expression to the following:

Yes it does. As long as the right side expression is made of constants (which fit into the required primitive type -- see @Jason's answer for what the JLS says about this exactly), you can do that. This will not compile because 128 is out of range:

byte a = 128;

Note that if you transform your first code snippet like this:

final byte a = 3; 
final byte b = 8; 
byte c = a + b;

it compiles! As your two bytes are final and their expressions are constants, this time, the compiler can determine that the result will fit into a byte when it is first initialized.

This, however, will not compile:

final byte a = 127; // Byte.MAX_VALUE
final byte b = 1;
byte c = a + b // Nope...

The compiler will error out with a "possible loss of precision".

fge
  • 110,072
  • 26
  • 223
  • 312
  • 1
    It's not correct. It has nothing to do with whether or not `3 + 8` is evaluated to `11` at compile-time. It's because the language specification clearly states that constants expressions of type `int` that can fit in a `byte` may be implicitly narrowed to `byte` at compile-time. – jason Jun 14 '13 at 14:42
  • 1
    @fge: Read what I quoted from the specification. It explicitly states that the value of the constant expression must be re presentable in the narrower type. In bold in my [answer](http://stackoverflow.com/a/17111090/45914). – jason Jun 14 '13 at 14:45
  • 2
    @Jason You missed the "_and the value of the constant expression is representable in the type of the variable._" so it is relevant that 3 + 8 evaluates to 11 and not to, say, 128. – Mark Rotteveel Jun 14 '13 at 14:46
  • 2
    @Jason Can you specifically quote the part of this answer that's incorrect? I'm having trouble following you. "As long as the right side expression is made of constants" seems to be in line with your JLS citation. – Paul Bellora Jun 14 '13 at 14:47
  • @Mark Rotteveel: I missed no such thing. It's in **bold** in my answer. – jason Jun 14 '13 at 14:48
  • 1
    @Paul Bellora: Because without the section of the specification that I drew attention to, an explicit cast would be required. It's the section of the specification that I quoted that explicitly permits the implicit narrowing conversion. – jason Jun 14 '13 at 14:48
  • Then I really don't understand what your problem is with this answer. – Mark Rotteveel Jun 14 '13 at 14:48
  • @Mark Rotteveel: Again, because without the section of the specification that I drew attention to, the compiler would not be permitted to do an implicit narrowing conversion. – jason Jun 14 '13 at 14:49
  • @Jason So your peeve is that fge doesn't reference the JLS? – Mark Rotteveel Jun 14 '13 at 14:50
  • @Jason I mentioned it: the compiler "can determine that the result will fit into a byte when it is first initialized" – fge Jun 14 '13 at 14:50
  • 1
    @Mark Rotteveel: No, it's not a peeve. What's important here is that the language explicitly permits implicit narrowing conversions. The compiler has to evaluate the constant expression to see if it fits, but without the explicit enabling of implicit narrowing conversions, it wouldn't matter. – jason Jun 14 '13 at 14:53
  • What does `final` have to do with the result? – Steve Kuo Jun 14 '13 at 16:20
  • 1
    @SteveKuo a `final` primitive type variable cannot change value during the JVM's lifetime. The compiler can therefore "retain" the value and, if it is a constant, use that value where this variable is referenced. – fge Jun 14 '13 at 16:23
  • @Steve Kuo: The definition of *constant expression* permits use of simple names that refer to constant variables ([§15.28](http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.28)) and variables that are initialized with constant expressions and are marked `final` are, by definition, constant variables ([§4.12.4](http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.4)). – jason Jun 14 '13 at 16:51
1

It is because 3 and 8 are compile time constants.

Therefore, at the time of compilation happens, compiler can identify that 3 + 8 can fit into a byte variable.

If you make your a and b to final (constant) variable. a + b will become a compile time constant. Therefore, it will compile without any issue.

    final byte a = 3;
    final byte b = 8;
    byte c = a + b;
Jude Niroshan
  • 3,973
  • 6
  • 36
  • 53