Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Converting from Java primitive to wrapper classes

I am mystified by the behavior of the Java compiler when assigning primitives to wrapper class references. Please see the code below. The lines with comments don't compile.

I don't understand the logic of why:

  1. a byte can be assigned to a Byte or Short, but not Integer or Long reference
  2. a short can be assigned to a Byte or Short, but not Integer or Long reference
  3. an int can be assigned to a Byte, Short, or Integer, but not Long reference
  4. a long can be assigned to a Long, but not Byte, Short or Integer reference

I cannot see the pattern. Any insight into this will be really helpful. Thanks.

Byte s5 = (byte)7;
Short s6 = (byte)7;
Integer s7 = (byte)7;   // Does not compile
Long s8 = (byte)7;      // Does not compile

Byte s9 = (short)7;
Short s10 = (short)7;
Integer s11 = (short)7; // Does not compile
Long s12 = (short)7;    // Does not compile

Byte s1 = (int)7;
Short s2 = (int)7;
Integer s3 = (int)7;
Long s4 = (int)7;       // Does not compile

Byte s13 = (long)7;     // Does not compile
Short s14 = (long)7;    // Does not compile
Integer s15 = (long)7;  // Does not compile
Long s16 = (long)7;
like image 469
user3516726 Avatar asked Feb 05 '15 03:02

user3516726


2 Answers

Let's look at the types of conversions allowed in an assignment context.

Principally:

Assignment contexts allow the use of one of the following:

  • an identity conversion

  • a widening primitive conversion

  • a widening reference conversion

  • a boxing conversion optionally followed by a widening reference conversion

  • an unboxing conversion optionally followed by a widening primitive conversion.

(Note my emphasis on one.)

Most of your examples that do not compile, for example

Integer s11 = (short)7;

require a widening primitive conversion followed by a boxing conversion. This is not a permitted conversion.

But then you might wonder why the following example does compile:

Byte s9 = (short)7;

This is a narrowing primitive conversion followed by a boxing conversion.

This is a special case:

In addition, if the expression is a constant expression of type byte, short, char, or int [...] a narrowing primitive conversion followed by a boxing conversion may be used if the type of the variable is:

  • Byte and the value of the constant expression is representable in the type byte.

  • Short and the value of the constant expression is representable in the type short.

  • Character and the value of the constant expression is representable in the type char.

This special case is necessary because there is no way to express an integer literal of a type narrower than int.

like image 114
Radiodef Avatar answered Sep 29 '22 07:09

Radiodef


This seems to be compiler-specific behavior. When I paste your code into Eclipse, running Java 7, I do not see the compiler errors you report for short to Integer or byte to Integer.

Instead, I see byte, short, and int can all be assigned to Byte, Short, and Integer, but not Long, and long can only be assigned to Long. Interestingly, if you change the variables to primitives instead of wrapper types, the byte, short, and int behavior doesn't change, but now the assignments from the other types to long also work.

javac 1.7.0_02
           | byte | Byte || short | Short || int | Integer || long | Long |
From byte  | Yes  | Yes  || Yes   | Yes   || Yes | No      || Yes  | No   |
From short | Yes  | Yes  || Yes   | Yes   || Yes | No      || Yes  | No   |
From int   | Yes  | Yes  || Yes   | Yes   || Yes | Yes     || Yes  | No   |
From long  | No   | No   || No    | No    || No  | No      || Yes  | Yes  |

Eclipse Indigo
           | byte | Byte || short | Short || int | Integer || long | Long |
From byte  | Yes  | Yes  || Yes   | Yes   || Yes | Yes     || Yes  | No   |
From short | Yes  | Yes  || Yes   | Yes   || Yes | Yes     || Yes  | No   |
From int   | Yes  | Yes  || Yes   | Yes   || Yes | Yes     || Yes  | No   |
From long  | No   | No   || No    | No    || No  | No      || Yes  | Yes  |

Given that different compilers allow different conversions, I suspect the "correct" behavior is not actually spelled out in the JLS. It seems certain conversions are done under the covers because the compiler writers considered it convenient (e.g. byte a = (int)1 is allowed but byte a = (int)1000 is not), not because it's a documented part of the language.

like image 20
dimo414 Avatar answered Sep 29 '22 08:09

dimo414