Why initialization of new Long Wrapper with int literal is valid? - java

Looking at the Java Doc for Long, it only has two constructors:
1) primitive long as param --> new Long(10L);
2) String as param --> new Long("23");
But this works
new Long(23);
But if the literal is more than the int MAX VALUE (2147483647), the L suffix becomes mandatory so this: (
new Long(2147483648) will now require an L after the value
However:
new Long(Integer.MAX_VALUE + 1) is OK
Who can explain this?

The first part of your question is straightforward: passing int where long is required is allowed because any number that can be represented as an int can also be represented as a long. This is a so-called widening conversion.
The second part, about new Long(Integer.MAX_VALUE + 1), is trickier. Although the widening conversion occurs here as well, it is not of the value that one might think: if you run this program
Long x = new Long(Integer.MAX_VALUE + 1);
System.out.println(x);
you get -2147483648, not the expected 2147483648, because of int overflowing on addition (demo).
The compiler is not smart enough to promote parts of the expression to long before performing addition. Widening conversion occurs after the addition has been performed, on the result of the addition with an overflow.

You can find the answer in javadocs:
A widening primitive conversion from an integral type to another integral type, or from float to double in a strictfp expression (§15.4), does not lose any information at all; the numeric value is preserved exactly.
The integer value 23 can be converted to long 23L without loss of any information at all. Thus with the use of through widening primitive conversions, JVM implicitly converts it to 23L.
So when you call
new Long(23)
It becomes
new Long(23L)

When we write new Long(23) , you can think of it as something like the following happening :
int i = 23;
long l = i; // widening primitive conversion
Long newLong = new Long(l);
Widening conversions are allowed as per the JLS 5.1.2 , so there are no issues with it.
When we try instead , new Long(2147483648) , the first step itself fails, as the value is more than what can fit in an int
int i = 2147483648; // doesn't fit in int
When we try new Long(Integer.MAX_VALUE + 1), you can think of it as something like the following happening:
int i = Integer.MAX_VALUE + 1; // This gives the INTEGER.MIN_VALUE -2147483648
long l = i ; // widening primitive conversion
Long newLong = new Long(l);
So, this is allowed. Hope this helps.

The differences come from using literal or not.
By using a literal,
long1 = new Long(2147483648)
The code will not compile as the compiler checks the validity of the literal again the type of the variable that receives the literal value and 2147483648 is out of range for an int. So the compiler emits an error.
It is valid for any literal.
As the compiler expects to have a specific type as value of the literals, so it does the check :
The type of a literal is determined as follows:
The type of an integer literal (§3.10.1) that ends with L or l is long
(§4.2.1).
The type of any other integer literal is int (§4.2.1).
...
By using a computation :
long1 = new Long(Integer.MAX_VALUE + 1);
the compiler doesn't check if the size of the Long argument constructor is in the int range.
It is not a literal declaration.
At runtime, an int to long widening primitive conversion occurs.
It produces so a int with Integer.MAX_VALUE + 1 that is -2147483648 (overflow of int) that is converted to a long value.

Related

Promotion of primitive types

I have a question about the promotion of primitive types in Java. As we can see in the following example, one of the methods does not compile due to an error of type mismatch. Each method returns the same value but in different types.
The version of primitive long method works without error while the version of wrapper class Long fails. This is because the int literal in the return statement will be first promoted to a broader primitive type (e.g. long) and then to the corresponding wrapper class Integer and so on. Since Integer is not a subclass of Long the compiler gives an error.
But why does the version of wrapper class Byte works without any error? What exactly does the compiler do at this point?
long getPrimitiveLong() {
return 12; // valid
}
Long getWrapperLong() {
return 12; // Error: type mismatch
}
Byte getWrapperByte() {
return 12; // valid
}
The version with Byte works through some compiler magic.
Unlike long numeric literals which can be constructed with a L suffix, e.g. 12L, there is no such thing as a byte literal. That is why Java compiler treats numeric literals that fit in a byte as byte literals. Hence, 12 in your last example is considered a constant of type byte.
Java Language Specification offers a description of this conversion in section 5.2:
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 is because Java allows 1 conversion or Autoboxing, not more.
Java can do all these:
int i = 5;
double d = i; // int to double
long l = i; // int to long
long l = d; // double to long
Or autobox:
Integer i = 5; // int to Integer
Double d = 5.0; // double to Double
Long l = 5L; // long to Long
Converting twice, say int to Double, gives Java a hard time.
number like 12 consider as int by default by the compiler that is why the error
To fix that you can use casting for byte and place L after the value of long variable.
Read following post for more details
http://javaseeeedu.blogspot.com/2015/12/casting-part-1.html
As a short answer - try to replace 12 with 128 (byte is in range -128 to 127).
It won't compile, right?
The outcome here is that the compiler knows about byte boundaries.
For the in-depth answer you can do a deep dive into OpenJDK.

Java: must cast to short, cannot use shorthand 'S'

I am trying to call a function that requires a short value. The following works:
i.setDamage((short) 10);
However, this does not:
i.setDamage(10S);
According to the IDE I am using, this should work. Why does it not? I am using Maven and Java 7.
According to the Java Language Specification, Section 3.10.1, the only integer type suffix for integer literals is L (or lower case l).
An integer literal is of type long if it is suffixed with an ASCII letter L or l (ell); otherwise it is of type int (§4.2.1).
The suffix L is preferred, because the letter l (ell) is often hard to distinguish from the digit 1 (one).
You'll just have to stick with a cast. (Although you may be able to just use the integer literal without a cast if the value is in range.)
According to the Java Language the long value will only hold the suffix of L.
Refer
Java Primitive Data types
JAVA doesn't provide any suffix like S or s for short. You need to cast to shortusing (short)100.
Possible values are
int num = 20; //Decimal
int num = 020; //octal
int num = 0x20; //Hexadecimal
int num = 0b1010; //binary
long num = 563L; //long
In JDK 7, you can embed one or more underscores in an integer literal like
int num = 19_90;

Java - Error on: long n = 8751475143;

This number falls into the long range, so why do I get the error:
Exception in thread "main" java.lang.Error: Unresolved compilation problem:
The literal 8751475143 of type int is out of range
Make it
long n = 8751475143L;
L will make it long literal
by default its int
An integer literal is of type long if it is suffixed with an ASCII letter L or l (ell); otherwise it is of type int (§4.2.1). The suffix L is preferred, because the letter l (ell) is often hard to distinguish from the digit 1 (one). [..]
The target of the assignment isn't taken into account when parsing the literal - so you need the L suffix:
long n = 8751475143L;
For the most part - and there are a few notable exceptions - the type of an expression is determined without much reference to its context. So as per section 3.10.1 of the JLS, an integer literal is of type int unless it has an l or L suffix, and the range of an integer literal of type int is of course limited to the range of int itslf.
All numbers in java are treated as integers, unless you say otherwise (or you use a decimal separator - then they are treated as a floats).
So, if you write
long i = 1234;
java will tread the number 1234 as integer, and do the type-cast to long for you.
However, if you type:
long n = 8751475143;
Java cannot treat 8751475143 as integer, because it's out of range. You need to specify, that what you meant was long, by adding 'L' at the end:
long n = 8751475143L;

Initialize a long in Java

Primitive Data Types - oracle doc says the range of long in Java is -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807.
But when I do something like this in my eclipse
long i = 12345678910;
it shows me "The literal 12345678910 of type int is out of range" error.
There are 2 questions.
1) How do I initialize the long with the value 12345678910?
2) Are all numeric literals by default of type int?
You should add L: long i = 12345678910L;.
Yes.
BTW: it doesn't have to be an upper case L, but lower case is confused with 1 many times :).
You need to add the L character to the end of the number to make Java recognize it as a long.
long i = 12345678910L;
Yes.
See Primitive Data Types which says "An integer literal is of type long if it ends with the letter L or l; otherwise it is of type int."
You need to add uppercase L at the end like so
long i = 12345678910L;
Same goes true for float with 3.0f
Which should answer both of your questions
To initialize long you need to append "L" to the end.
It can be either uppercase or lowercase.
All the numeric values are by default int. Even when you do any operation of byte with any integer, byte is first promoted to int and then any operations are performed.
Try this
byte a = 1; // declare a byte
a = a*2; // you will get error here
You get error because 2 is by default int.
Hence you are trying to multiply byte with int.
Hence result gets typecasted to int which can't be assigned back to byte.

Java - short and casting

I have the following code snippet.
public static void main(String[] args) {
short a = 4;
short b = 5;
short c = 5 + 4;
short d = a;
short e = a + b; // does not compile (expression treated as int)
short z = 32767;
short z_ = 32768; // does not compile (out of range)
test(a);
test(7); // does not compile (not applicable for arg int)
}
public static void test(short x) { }
Is the following summary correct (with regard to only the example above using short)?
direct initializations without casting is only possible using literals or single variables (as long as the value is in the range of the declared type)
if the rhs of an assignment deals with expressions using variables, casting is necessary
But why exactly do I need to cast the argument of the second method call taking into account the previous summary?
These are the relevant JLS sections:
JLS 5.1.1 Identity Conversion
A conversion from a type to that same type is permitted for any type.
JLS 5.2 Assignment Conversion
Assignment conversion occurs when the value of an expression is assigned to a variable: the type of the expression must be converted to the type of the variable. Assignment contexts allow the use of one of the following:
Identity conversion
[...]
In addition, if the expression is a constant expression 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 above rules explain all of the following:
short a = 4; // representable constant
short b = 5; // representable constant
short c = 5 + 4; // representable constant
short d = a; // identity conversion
short e = a + b; // DOES NOT COMPILE! Result of addition is int
short z = 32767; // representable constant
short z_ = 32768; // DOES NOT COMPILE! Unrepresentable constant
As to why this doesn't compile:
test(7); // DOES NOT COMPILE! There's no test(int) method!
It's because the narrowing conversion with constant is only defined for assignments; not for method invocation, which has entirely different rules.
JLS 5.3. Method Invocation Conversion
Method invocation conversions specifically do not include the implicit narrowing of integer constants which is part of assignment conversion. The designers of the Java programming language felt that including these implicit narrowing conversions would add additional complexity to the overloaded method matching resolution process.
Instead of explaining how method resolution works precisely, I will just quote Effective Java 2nd Edition, Item 41: Use overloading judiciously:
The rules that determine which overloading is selected are extremely complex. They take up thirty-three pages in the language specification, and few programmers understand all of their subtleties.
See also
Varying behavior for possible loss of precision
short x = 3; x += 4.6; compiles because of semantics of compound assignment
The result of an arithmetic operation on short values is always int. test(7) doesn't work, since you haven't said that 7 is of type short. The compiler should be a bit smarter here.
The '7' in the call test(7); is an int and will not be automatically converted to a short.
It works when you declare and initialize short values, but that's a special case for the compiler. This special case doesn't exist for method calls.

Categories