52

Why does

char ch = '4';
ch = '4' + 2;

work, but

char ch = '4';
ch = ch  + 2;

doesn't?

Lester
  • 1,830
  • 1
  • 27
  • 44
  • 24
    Hint: the first case is a constant value. The second isn't. – Jon Skeet Jul 07 '14 at 10:18
  • 6
    In particular, see http://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.2 " 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." - Will add this as an answer if I get time to do it properly. – Jon Skeet Jul 07 '14 at 10:21
  • 1
    Related: http://stackoverflow.com/questions/8688668/in-java-is-the-result-of-the-addition-of-two-chars-an-int-or-a-char – Marco13 Jul 07 '14 at 10:39
  • [This answer](http://stackoverflow.com/a/21187200/1686291) may help. – Not a bug Jul 07 '14 at 12:38
  • Actually you can't even add a char and a char. E.g., `ch = ch + ch;` doesn't compile. See linked q above for reason. – Boann Jul 07 '14 at 14:47
  • The problem is not in the addition but in the assignment. When doing the addition, the char is promoted to int and the whole expression is an int. Which cannot be assigned to a char variable. –  Jul 08 '14 at 22:29

1 Answers1

62

To understand this, lets consider what the compiler does at each step for both possibilities. Lets start with:

ch = '4' + 2;

The compiler converts '4' to an int. So it becomes

ch = 52 + 2;

Which the compiler then turns into

ch = 54;

ch is a char, and the compiler is allowed to convert 54 to a char as it can prove that there is no loss in the conversion.

Now lets consider the second version:

ch = ch  + 2;

ch has no known value at compile time. Thus this becomes

ch = ((int) ch) + 2;

Now the compiler cannot prove that the result of this (an int) is storable within the range of a char. So it will not automatically narrow it, and reports it as an error.

EDIT1:

If the compiler can prove that the variable will never change, and is inlineable. Then the second form can be turned into the first. Subir pointed out that adding 'final' makes this possible. Although if a compiler was to perform change analysis then it is technically capable of figuring this out without the final keyword, but final does make it easier for the compiler and readers of the code.

EDIT2:

Narrowing of int to char is covered in the Java Language Spec, the link was kindly provided by Jon Skeet.

Chris K
  • 11,622
  • 1
  • 36
  • 49
  • Yes, the main difference is compile time & run time conversions. – Subir Kumar Sao Jul 07 '14 at 10:26
  • 1
    To support the answer: `final char c = '4'; ch = c + 2;` will work too. – Timuçin Jul 07 '14 at 10:28
  • You might want to add a link to the spec, too. – Jon Skeet Jul 07 '14 at 10:31
  • Yes, I missed that. We can't reassign a final variable. But the point is if it is known at compile time, the compiler will not give error. – Timuçin Jul 07 '14 at 10:31
  • 1
    @Tim, you surely meant that `final char a = '4'; char b = a + 2;` will work, while `char a = '4'; char b = a + 2;` will not. – Bartek Maraszek Jul 07 '14 at 10:33
  • Why doesn't it just overflow, if out of range? Just like `char ch = 65535; ++ch;` will work so `ch` is 1 again. – Lester Jul 07 '14 at 10:41
  • 2
    @Lester because an overflow of a character was viewed by the language designers as a clear case defect; and one that they could detect in this case at compile time. Notice that that is not the case for ints. – Chris K Jul 07 '14 at 10:43