I had a look to this post Java instantiate Short object in Java but did not exactly respond to what I am looking for.
Does anybody know why the first line (//1) gives an error whereas the second line (//2) does not
Short s = new Short(4);//1
short s1 = 4;//2 Here I know why it works it gets
//implicitly narrow converted into a short.
As stated in the code, I understand why the second line works fine, but what about the first line? Where is the sense of writing Short s = new Short((short)4);
Bottom line: why does it not cast it implicitly? It is a constant not a variable.
Thanks in advance.
You could define a constructor taking a 'short' and another taking an 'int' and language semantics would make your call to the constructor ambiguous. So you need to use strict type. Also, since Short is final, try using Short.valueOf((short) 4) to avoid unnecessary object allocation.
This is caused because Java use "search mechanizm" to find a constructor for int type. When you assing value to varialbe its type is defined and compiler can optimize the code. With constructor (or any other method) this is not possible.
When you say
Short s = new Short(4);
you are specifying an integer literal 4, not a short literal, and this is not permitted under the Java language specification for Method Invocation Conversion.
However, when you say
short s1 = 4;
this is subject to what the Java Language Specification calls a Narrowing Primitive Conversion, which is permitted even if it would lose precision.
You should do like below :
Short s = new Short((short) 4);
Reason :
Type of 4 is Integer/int and NOT Short. So you need to do explicit typecast to ensure that Short constructor takes compatible type.
It's just not supported.
With the other number object types, there is a literal for the value, numbers are int by default, but for long you have the L suffix, double has d and so on.
If you were to have new Short(int) throw an exception, then the constructor would be inconsistent with the other number constructors.
Trying to create a short primitive that is too big is tested at compile time, the same with byte.
If you did this:
byte b = 1000000;
Your code would not compile, rather than throw an exception.
So the behaviour is consistent when you take the compiler into account.
Related
Why can I do this:
short a = 5;
But not this:
void setNum(short a);
setNum(5);
It throws:
Possible lossy conversion from int to short
I understand that 5 is an integer literal and you have to cast it. I also understand that if the value is not a constant then is obvious that it needs to throw that error because maybe the value reaches the limit of a short type. But why if the compiler knows I'm passing a constant that a short can hold (as in the assignment) it doesn't let it compile? I mean, what is the difference between them?
In order to understand why the assignment type-conversion works whilst the invocation one is rejected, one has to refer to the Java Language Specification topic for both narrowing primitive conversions and the context of that conversion: assignment context and invocation context.
According to the JLS, the narrowing primitive conversion is allowed in assignment context if:
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.
... which matches your case with int constant 5 when assigned to short a.
No such narrowing primitive conversion is allowed in the invocation context, which explains why your call to setNum(short) fails when passed the int constant 5.
But why if the compiler knows I'm passing a constant that a short can hold (as in the assignment) it doesn't let it compile? I mean, what is the difference between them?
The JLS must not have wanted to burden compilers with this additional logic. In the invocation case, the argument which is type-matched to the formal parameter is an expression - the compiler already has to determine the type, but it shouldn't need to also check if the expression's value can also safely be narrowed. In this case, being a constant, it is clear to us that it can be, but in execution context the compiler is allowed to not bother with that check, and is in-fact correct to disallow it.
It should be fairly clear that when expressions are allowed, it would be much easier for bugs to creep in where narrowing cannot be done without losing precision, so the JLS and compilers just disallow it in all cases.
The same is true in numeric context, so the declaration:
short a = 5;short b = a * 5;
... is similarly not allowed, despite being clearly comprised of constants which narrow correctly.
When you type 5, this is automatically an integer. I'm not sure what IDE you are using that's giving you the error, but the reason it's warning you is because you're converting a larger storage capacity value to a smaller one, which although not in your case, could result in you losing data. This is called a narrowing conversion.
Integers can hold 32 bits of data, while shorts can only hold 16 bits of data. So, for example (in reality the numbers would be much bigger), an int had a value equal to 50, and you then cast it to a short, the data would be cut to "5" because a short doesn't have a large enough memory allocation.
That code you posted won't work because when you define the short like follows:
short a = 5;
You're directly creating a short, and the number is small enough that the short can hold it. When you type "5" alone as a method argument, it's handled as an integer, and the JVM doesn't know that it's small and safe to make a short. To make the "5" suitable as an argument for the method, you need to turn it into a short using a narrowing conversion, as follows:
setNum((short) 5);
But as stated, if you don't actually know the value of the int, and you're not sure it's small enough to be turned into a short, this can create errors in your code as some of the number will be chopped off.
(Here is some Oracle documentation on this)
I meet a method invocation which the parameter type is short, such as
foo (short s){...}
When I call it, I think of the two solution:
one is:
foo((short) 1);
and another:
short s = 1;
foo(s);
What's the difference between them, and which is better?
1 will be by default treated as an int in Java.
So, when you're doing ((short)1), the you're passing 1 as a short parameter through the function. The receiving argument should be of a short type.
Whereas, when you have short s =1, then it is obviously a short integer.
Remember that in both the case, shorts will be converted to int(this is called type promotion) while performing operations. And, if operated with double operands, those int's will get promoted to double.
What's the difference between them, and which is better?
Both are doing the same operations finally(passing a short variable as an argument), and both of them are of equally better. But, you should prevent using short integers, unless extremely required as it has some shortcomings(causes compile-time errors when you accidentally try to store an int/long to a short).
short s = 1 is defining a variable as a short with a value of 1.
(short) 1 is casting an int value of 1 to the primitive datatype short.
There is no difference. They both create a short with a value of 1.
See Java: Primitive Data Types for information about a short and its value range, though, as setting a short to a value that is above or below its prescribed range will throw an exception.
Both solutions will work, and performance differences between the two will be hardly noticable.
(short) 1; uses a literal, which gets casted to a short that is passed in as a parameter for the function foo.
short s = 1; actually creates a variable in memory prior to passing the value as a parameter, in case you want to also use the variable for other things.
In java up cast from short to int automatically happens.
But for down casting you have to manually cast it.
short(integer_value)
(short) 1
If you call using casting you can send integer parameter too.
The following seemingly trivial problem has shaken the core of my understanding on how primitives work in Java.
I have come across the term "implicit narrowing" whereby a variable of a smaller-range type is allowed to hold a literal value of a larger-range type, if that value falls within that smaller-range. As I know, Java permits that only among byte, char, short, and int.
For example, a byte variable CAN take an int if that value is small enough to fit the range of the byte type.
byte b1 = 3; // allowed even though 3 is an int literal
byte b2 = 350; // compilation error because a byte cannot go beyond positive 127
So, this works fine:
byte k = 3;
But I don't know why the line below does not work!!
Byte k = new Byte(3);
Unless I change the latter to Byte k = new Byte((byte)3), I get this compilation error:
error: no suitable constructor found for Byte(int)
Byte k = new Byte(3);
^
constructor Byte.Byte(byte) is not applicable
(actual argument int cannot be converted to byte by method invocation conversion)
The last portion of the error message seems to have a clue, which says:
"... actual argument int cannot be converted to
byte by method invocation conversion"
Then my question about the clue becomes:
why?! I mean, what is the difference between assigning a small int literal to a byte and passing a small int literal to a method to be captured by a method parameter of type byte?
I understand that casting would have to be used if I passed an int variable. But, I am NOT passing a variable. Rather, I am passing a small literal which the compiler should realize that it is small enough for a byte!!
The rules are different for assignment and method calls (including constructors) for literals.
According to the Java Language Specification (JLS) 8 §3.10, Java only has 6 literal types: IntegerLiteral, FloatingPointLiteral, BooleanLiteral, CharacterLiteral, StringLiteral, and NullLiteral.
3.10.1 further specifies:
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).
(§4.2.1 is just a specification of the ranges of types)
There are nearly 7 pages about IntegerLiteral, so I'm not going to go through the whole thing. Suffice it to say that int literals are downcast to byte and short as appropriate during assignment.
However, its usage in a constructor is entirely different. Since a constructor is a method, the normal rules for its arguments apply.
I tried quickly sorting through the rules for matching in the JLS, but its a very long and complicated section. Needless to say, only widening conversions will happen automatically when choosing a method to run. i.e. you can pass an int to a method expecting a long without an explicit cast, but you can't pass an int to a method expecting a byte unless you explicitly cast it.
I would have to agree that it does seem similar, but to the compiler, these are two very different things. The first is, as you said, using implicit narrowing to cast the value. In the second however, you're using a constructor with a particular signature. That signature requires you to provide a byte. Think of it this way: what happens if they added a constructor, public Byte(int i)? Now all of the sudden, you're changing your old code's meaning if they were to allow this. I think this particular instance is why this is not allowed, although there may be other additional compilations.
Integer literals are implicitly downcast to byte, char, short and int as appropriate during ASSIGNMENTS.
Therefore byte b1 = 3; compiles fine.
Note that integer literals are by default of type int, so unless you either assign an integer literal to a variable of type byte or explicitly cast it to a byte, compiler consider the literal itself to be an int.
That is the reason why you get a compilation error on Byte k = new Byte(3); Here you are using an int in the constructor which requires a byte.
Byte k=new Byte(3);
1) The above statement will create a object of type "Byte", remember that implicit narrowing can only occur for primitive's.
2) While creating object of type Byte, the constructor will consider the argument as Integer, for that reason we have to explicitly cast the argument to byte as:
Byte k=new Byte((byte)3);
I know how to use casting in Java but have a more specific question; could you please explain to me how the casting works (in memory)?
How is the variable type changed upon upcasting and downcasting?
How does the JVM know that it's safe to send this method to this object?
Thank you in advance.
Could you please explain me how the casting works ( in memory )?
It works at byte code level not really in memory
How the variable type is changed on upcasting and downcasting?
If it is a primitive with an special bytecode instruction, for instance from long to integer as in:
long l = ...
int i = ( int ) l;
The bytecode is: l2i if is a reference with the instruction checkcast
How the JVM knows that from this time it's safe to send this method to this object?
It doesn't, it tries to do it at runtime and if it fails throws an exception.
It is legal to write:
String s = ( String ) new Date();
Possible duplicate of the accepted answer to this question:
How does the Java cast operator work?
There's also quite an extensive explanation here, that covers all data types, etc.:
http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.5
All casting of primitives is done in registers (like most operations) For primitives, in many cases, when down casting the lower bits are taken and for up casting, the sign is extended. There are edge cases but you usually don't need to know what these are.
upcasting/downcasting a reference works the same in that it checks the actual object is an instance of the type you cast to. You can cast which is neither upcast nor down cast.
e.g.
Number n = 1;
Comparable c = (Comparable) n; // Number and Comparable are unrelated.
Serializable s = (Serializable) c; // Serializable and Comparable are unrelated.
If you are interested in the inner workings of jvn concerning how casts work you may also check the jvm spec http://java.sun.com/docs/books/jvms/second_edition/html/Concepts.doc.html#25611
Whenever I use byte or short data type as a method parameter, on method calls, I am required to explicitly cast the values I pass to those methods.
To better explain:
void foo(short x)
{}
void main() {foo((short)32);}
If I dont use short here then warning is generated.
method foo in class px cannot be applied to given types
required: byte
found: int
How can I get it better?
No way out.
Java doesn't have a way to code byte or short literals. Any number literal is an int value and converting an int to a short without casting always creates a warning.
Integer literals implicitly have the type int, and converting from an int to byte or short potentially loses information, so it requires explicit casting.
So don't use byte or short unless you really need to, which is very rarely the case.