Why when casting in inline with assign a value it compiles? - java

This is my Example code
class Example{
public static void main(String args[]){
short s = 100;
byte b = s; // this is compile time error because of no casting
byte z = (short)25.12; // why this happens
System.out.println(z);
System.out.println(((Object)z).getClass());
}
}
please forgive for me as i just begin to learn java.
can you please tell me this problem in technically.

Your this line byte b = s; gives error because you can't assign bigger data type short(2 bytes) to smaller data type byte(1 byte). That is why it is giving you compile time error.
Now coming to this line byte z = (short)25.12; lets break it down. 25.2 is by default double but when you typecast it using (short) it becomes 25 which is an integral literal. Now compiler will check whether 25 can be stored in byte or not. And since 5 is between -128 and 127 i.e. the range of byte it won't give any compile time error. But if you give any value out of the range of byte it will give error. Eg: byte z = (short)128.12;

The compiler doesn’t run your source code or analyze its logic. The compiler merely creates bytecode from each line of your source code.
When the compiler sees byte b = s;, it just tries to generate code that assigns the value of b. It doesn’t look back through your code to see what other instructions may have affected the value of s.
It only knows that on that line of code, you are trying to set an 8-bit value (a byte) to an unknown 16-bit value (a short). When the compiler is looking at this line of code, it does not consider any previous lines, so it doesn’t care about the prior assignment; it treats s as an unknown 16-bit value. That unknown value might or might not be something that fits in 8 bits.
On the other hand, byte z = (short)25.12; is safe because the compiler doesn’t need to look at any other instructions to know that 25 can fit into 8 bits. It doesn’t matter that you have cast it to a short. What matters is that implicitly casting it to byte will not lose any information.
This would be true for (int) 25 as well. In all cases, 25 will fit in 8 bits without losing information.

Typecasting a bigger datatype to a smaller one is not implicit because it can result in an incorrect conversion (loss of data).
byte b = s;
That's why the above line wont't compile because s is of type short (2 bytes) and b is of type byte (1 byte). Hence you need to typecast it explicitly
byte b = (byte)s;

Take a look this article: https://www.javatpoint.com/java-data-types
You getting errors, because:
short takes 16 bit(2 byte)
byte takes 8 bit (1 byte)
You can't put 16 bits, in to 8 bits.

Related

In java, how come method read() from FileInputStream works does not throw " incompatible types: possible lossy conversion"?

I am currently going through Java I/O tutorial and having hard time understanding the read() method of FileInputStream class. I know that per documantion that read() method reads "byte" of data from stream and returns an integer representing the byte (between 0 and 256) or -1 if it reaches the end of file.
Byte in java has a range between -128 and 127, so, how come when I edit xanadu.txt and add ASCI symbol "ƒ" (which has a decimal value of 131), java does not complain by throwing an error that value 131 is out of range defined by byte (-128 and 127)? When I try to test this using literals I get two different results.
The following works:
byte b = 120;
int c = b;
System.out.println((char)c);
Output: x
But this does NOT work (even though it works when added to xanadu.txt):
byte b = 131;
int c = b;
System.out.println((char)c);
Output: error: incompatible types: possible lossy conversion from int to byte
byte b = 131;
I tried explicitly casting using byte: (how is this possible?)
byte b = (byte)131;
int c = b;
System.out.println((char)c);
Output: テ
I am total newbie when it comes to I/O streams, somebody please help me understand it.
EDIT: Turns out my knowledge on concepts of type casting was lacking, specifically in understanding the difference between "Widening" and "Narrowing". Reading up more about these concepts helped me understand why explicit (aka narrowing) casting works.
Allow me to explain: Look at the 3rd code block where I am explicitly casting the literal '131' to type of byte. If we are to convert the literal 131 into binary form of 32-bit signed 2's complement integer, we will get 00000000 00000000 00000000 10000011 which is 32-bits or 4 bytes. Recall that Java data type 'byte' can only hold 8-bit signed 2's complement integer, so, 131 is out of range and thus we get error "possible lossy conversion from int to byte". But, when we explicitly cast it to byte, we are 'chopping off' or correct term would be 'narrowing' the binary down to 8 bit integer. So, when we do that, then the resulting binary is 10000011 which is -125 in decimal value. Since -125 is in range of -128 and 127, byte has no issues accepting and storing it. Now when I try to story the value of byte in int c, implicit or "widening" casting takes place, where -125 in binary form of 8-bit 10000011 is converted into equivalent -125 in binary form of 32-bit 11111111 11111111 11111111 10000011. Finally, system.out is trying to output the value of (char)c which is another explicit or "narrowing" casting where its trying to shrink from 32-bit signed to 16-bit unsigned. When casting is complete, we get 11111111 10000011 in binary form. Now, when this binary is converted into character form by java, it returns テ.
I can conclude by saying that it helps converting everything into binary form and go from there. But make sure you understand encoding and 2's complement
I don't know where you got the value 131 from, but as far as I am concerned, LATIN SMALL LETTER F WITH HOOK (ƒ) is not in the original ASCII character set, but in extended ASCII, with a decimal value of 159. See here. It is also encoded in UTF-16 (how Java chars are encoded) as hex 192 (decimal value 402).
First, ensure that your text files are encoded in extended ASCII, and not UTF-8 (which is the most likely encoding). Then you can use a FileInputStream to read the file, and you will get 159.
Note that 159 is outside the range of the the Java byte type. This is fine, because read returns an int. If the text file is encoded in UTF-8 however, ƒ is encoded in 2 bytes, so read will be reading one byte at a time.
Your second code block doesn't work because as you said, byte goes from -128 to 127, so 131 obviously doesn't fit.
Your third code block forces 131 into a byte, which causes overflow and the value "wraps back around" to -125. b and c are both -125. When you cast this to a char it becomes 65411 because this conversion involves padding the whole number to 16-bits first, then treating it as an unsigned integer.
The reason why this all works when you use FileInputStream.read instead of doing these conversions yourself, is because read actually returns an int, not a byte. It's just that the int it returns will always be in the range -1 ~ 255. This is why we say "read returns a byte", but its actual return type is int.
byte b = 131; // this is 8 bits type, but >8 bits value
int c = b; // this is 32 bits type
System.out.println((char)c); // this is 16 bits type
Output: error: incompatible types: possible lossy conversion from int to byte
byte b = 131;
The two-complement encoding of 131 is:
2^7+2^1+2^0
^^^
sign bit
131 won't fit in a signed byte without an overflow in the two complement representation that is used for signed types. The highest bit=sign bit is set which gets extended when casting from byte to int.
The Java compiler notices that 131 won't fit properly in a byte which leads to the error.

Why does the compiler not give an error for this addition operation? [duplicate]

This question already has answers here:
Why don't Java's +=, -=, *=, /= compound assignment operators require casting?
(11 answers)
Closed 5 years ago.
I know that the compiler does implicit type conversion for integer literals.
For example:
byte b = 2; // implicit type conversion, same as byte b = (byte)2;
The compiler gives me an error if the range overflows:
byte b = 150; // error, it says cannot convert from int to byte
The compiler gives the same error when the variable is passed an expression:
byte a = 3;
byte b = 5;
byte c = 2 + 7; // compiles fine
byte d = 1 + b; // error, it says cannot convert from int to byte
byte e = a + b; // error, it says cannot convert from int to byte
I came to the conclusion that the result of an expression that involves variables cannot be guaranteed. The resulting value can be within or outside the byte range so compiler throws off an error.
What puzzles me is that the compiler does not throw an error when I put it like this:
byte a = 127;
byte b = 5;
byte z = (a+=b); // no error, why ?
Why does it not give me an error?
While decompiling your code will explain what Java is doing, the reason why it's doing it can be generally found in the language specification. But before we go into that, we have to establish a few important concepts:
A literal numeral is always interepreted as an 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).
A byte can only hold an integer value between -128 and 127, inclusive.
An attempt to assign a literal that is larger than the type that can hold it will result in a compilation error. This is the first scenario you're encountering.
So we're back to this scenario: why would adding two bytes that are clearly more than what a byte can handle not produce a compilation error?
It won't raise a run-time exception because of overflow.
This is the scenario in which two numbers added together suddenly produce a very small number. Due to the small size of byte's range, it's extremely easy to overflow; for example, adding 1 to 127 would do it, resulting in -128.
The chief reason it's going to wrap around is due to the way Java handles primitive value conversion; in this case, we're talking about a narrowing conversion. That is to say, even though the sum produced is larger than byte, the narrowing conversion will cause information to be discarded to allow the data to fit into a byte, as this conversion never causes a run-time exception.
To break down your scenario step by step:
Java adds a = 127 and b = 5 together to produce 132.
Java understands that a and b are of type byte, so the result must also be of type byte.
The integer result of this is still 132, but at this point, Java will perform a cast to narrow the result to within a byte - effectively giving you (byte)(a += b).
Now, both a and z contain the result -124 due to the wrap-around.
The answer is provided by JLS 15.26.2:
For example, the following code is correct:
short x = 3;
x += 4.6;
and results in x having the value 7 because it is equivalent to:
short x = 3;
x = (short)(x + 4.6);
So, as you can see, the latest case actually work because the addition assignment (as any other operator assignment) performs an implicit cast to the left hand type (and in your case a is a byte). Extending, it is equivalent to byte e = (byte)(a + b);, which will compile happily.
I came to the conclusion that the result of an expression that involves variables cannot be guaranteed. The resulting value can be within or outside the byte range so compiler throws off an error.
No, that's not the reason. The compilers of a staticly-typed language work in this way: Any variable must be declared and typed, so even if its value is not known at compile-time, its type is known. The same goes for implicit constants. Based upon this fact, the rules to compute scales are basically these:
Any variable must have the same or higher scale than the expression at its right side.
Any expression has the same scale of the maximum term involved on it.
An explicit cast forces, of corse, the scale of the right-side expression.
(These are in fact a simplified view; actually might be a little more complex).
Apply it to your cases:
byte d = 1 + b
The actual scales are:
byte = int + byte
... (because 1 is considered as an implicit int constant). So, applying the first rule, the variable must have at least int scale.
And in this case:
byte z = (a+=b);
The actual scales are:
byte = byte += byte
... which is OK.
Update
Then, why byte e = a + b produce a compile-time error?
As I said, the actual type rules in java are more complex: While the general rules apply to all types, the primitive byte and short types are more restricted: The compiler assumes that adding/substracting two or more bytes/shorts is risking to cause an overflow (as #Makoto stated), so it requires to be stored as the next type in scale considered "safer": an int.
The basic reason is that the compiler behaves a little differently when constants are involved. All integer literals are treated as int constants (unless they have an L or l at the end). Normally, you can't assign an int to a byte. However, there's a special rule where constants are involved; see JLS 5.2. Basically, in a declaration like byte b = 5;, 5 is an int, but it's legal to do the "narrowing" conversion to byte because 5 is a constant and because it fits into the range of byte. That's why byte b = 5 is allowed and byte b = 130 is not.
However, byte z = (a += b); is a different case. a += b just adds b to a, and returns the new value of a; that value is assigned to a. Since a is a byte, there is no narrowing conversion involved--you're assigning a byte to a byte. (If a were an int, the program would always be illegal.)
And the rules say that a + b (and therefore a = a + b, or a += b) won't overflow. If the result, at runtime, is too large for a byte, the upper bits just get lost--the value wraps around. Also, the compiler will not "value follow" to notice that a + b would be larger than 127; even though we can tell that the value will be larger than 127, the compiler won't keep track of the previous values. As far as it knows, when it sees a += b, it only knows that the program will add b to a when it runs, and it doesn't look at previous declarations to see what the values will be. (A good optimizing compiler might actually do that kind of work. But we're talking about what makes a program legal or not, and the rules about legality don't concern themselves with optimization.)
I have encountered this before in one project and this is what I learned:
unlike c/c++, Java is always use signed primitives. One byte is from -128 to +127 so if you assign anything behind this range it will give you compile error.
If you explicitly convert to byte like (byte) 150 still you won't get what you want (you can check with debugger and see it will convert to something else).
When you use variables like x = a + b because the compiler doesn't know the values at run time and cannot calculate whether -128 <= a+b <= +127 it will give error.
Regarding your question about why compiler doesn't give error on something like a+=b :
I dig into java compiler available from openjdk at
http://hg.openjdk.java.net/jdk9/jdk9/langtools.
I traced the tree processing of operands and came to an interesting expression in one of the compiler files Lower.java which partially responsible for traversing the compiler tree. here is a part of the code that would be interesting (Assignop is for all of the operands like += -= /= ...)
public void visitAssignop(final JCAssignOp tree) {
...
Symbol newOperator = operators.resolveBinary(tree,
newTag,
tree.type,
tree.rhs.type);
JCExpression expr = lhs;
//Interesting part:
if (expr.type != tree.type)
expr = make.TypeCast(tree.type, expr);
JCBinary opResult = make.Binary(newTag, expr, tree.rhs);
opResult.operator = newOperator;:
....
as you can see if the rhs has different type than the lhs, the type cast would take place so even if you declare float or double on the right hand side (a+=2.55) you will get no error because of the type cast.
/*
* Decompiled Result with CFR 0_110.
*/
class Test {
Test() {
}
public static /* varargs */ void main(String ... arrstring) {
int n = 127;
int n2 = 5;
byte by = (byte)(n + n2);
n = by;
byte by2 = by;
}
}
After decompilation of your Code
class Test{
public static void main(String... args){
byte a = 127;
byte b = 5;
byte z = (a+=b); // no error, why ?
}
}
Internally, Java replaced your a+=b operator with (byte)(n+n2) the code.
The expression byte1+byte2 is equivalent to (int)byte1+(int)byte2, and has type int. While the expression x+=y; would generally be equivalent to var1=var1+var2;, such an interpretation would make it impossible to use += with values smaller than int, so the compiler will treat byte1+=byte2 as byte1=(byte)(byte1+byte2);.
Note that Java's type system was designed first and foremost for simplicity, and its rules were chosen to as to make sense in many cases, but because making the rules simple was more important than making them consistently sensible, there are many cases where the type system rules yield nonsensical behavior. One of the more interesting ones is illustrated via:
long l1 = Math.round(16777217L)
long l2 = Math.round(10000000000L)
In the real world, one wouldn't try to round long constants, of course, but the situation might arise if something like:
long distInTicks = Math.round(getDistance() * 2.54);
were changed to eliminate the scale factor [and getDistance() returned long].
What values would you expect l1 and l2 should receive? Can you figure out why they might receive some other value?

Assigning result of an expression to a primitive

K.Sierra in her book "SCJP Study Guide" mentions "We know that a literal integer is always an int, but more importantly, the result of an expression involving anything int-sized or smaller is always an int."
I've started experimenting and I'm a little bit confused with the below results:
byte a = 1; // correct
byte b = 1 + a; // incorrect (needs explicit casting)
byte c = 1 + 1; // correct (I expected it to be incorrect)
Could anyone explain to me why the last example does not require casting? Why does the Java compiler put the implicit cast? Is it because there are 2 int literals? Clarification greatly appreciated.
Implicit typecasting only works when the value of your RHS is known at compile-time, means they are compile-time constants. In other cases, you need to do explicit typecasting.
So: -
byte c = 1 + 1; // Value of `1 + 1` is known at compile time. Implicit cast
byte c = 1 + a; // Value of `1 + a` is evaluated at runtime. Explicit cast needed
Also, note that, if you declare your variable a as final byte a = 1, then the 2nd assignment will compile, as in that case, your a will be a compile time constant.
Yes, it is because they are literals, meaning they are compile-time constants and the compiler ensures that the size of the result is indeed a byte. The same will fail if you exceed the byte range. Try assigning 128 to c or, for that matter, 1 << 7 or any other compile-time constant greater than 127.
As said "The result of an expression involving anything int-sized or smaller is always an int"
So byte b = 1 + a; returns an int value which is not evaluated at complie time. Hence Compiler cannot check it if the result falls within the byte range and expects us to put an explicit cast.

'byte' type in Java [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Literal Syntax For byte[] arrays using Hex notation..?
I am trying to create a byte array of size '1' that holds the byte 0x0. In Java, can I just do something like this:
byte[] space = new byte[1];
space[0] = 0x0;
Will the hex value 0x0 be converted to its byte rep of 00000000 and store it in space[0]?
Will the hex value 0x0 be converted to its byte rep of 00000000 and store it in space[0]?
In your executing program, there is only one representation of the Java byte value zero - the 8 bit 2's complement representation of the integer zero - 8 zero bits.
It doesn't make any difference whether you express the number literal in your source code as 0 (decimal) or 00 (octal) or 0x0. They all mean exactly the same thing.
So - Yes, your code does what you expect.
A simpler one line version would be:
byte[] space = new byte[] {0};
or
byte[] space = {0};
or even
byte[] space = new byte[1];
(The last one relies on the fact that Java byte arrays are default initialized to all zeros.)
Will the hex value 0x0 be converted to its byte rep of 00000000 and store it in space[0]?
Sort of yes. Technically (i.e. according to the JLS) what happens is this:
0x0 becomes an int literal. (The specific syntax used for the integer literal is actually immaterial at this point ... so long as it is valid.)
The int literal is narrowed using an implicit primitive narrowing conversion to a byte value.
The byte value is used in the initialization of the array.
The implicit narrowing in step 2 is allowed because:
This is in an assignment context.
The value being assigned is a compile time constant expression of an integer type. (A literal is a compile time constant expression.)
The actual value being assigned is in the range of the type it is to be assigned to. (In this case, -128 to +127.)
In layman's terms, the compiler "knows" that there will never be any loss of information in the narrowing conversion.
Yes this will do exactly what you think it should do. Also see this related question.

Conversions and Promotions

I just wondering why this works (in Java):
byte b = 27;
but being method declared like this:
public void method(byte b){
System.out.println(b);
}
This doesn't work:
a.method(27);
Gives a Compiler error as follows:
`The method method(byte) in the type App is not applicable for the arguments (int)`
Reading this doesn't give me any clue (probably i am missunderstanding something).
Thanks in advance.
The reason the assignment
byte b = 27;
works is due to section 5.2 of the Java Language Specification (assignment conversion), which includes:
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.
In other words, the language has special provision for this case with assignments. Normally, there's no implicit conversion from int to byte.
Interestingly, C# works differently in this respect (despite being like Java in so many other aspects of core functionality) - the method call is valid in C#.
Simple answer - a byte is 1 byte of memory, and an integer is typically 4 bytes.
Without explicitly trying to cast the int to a byte, there is no implied coercion because an integer of size say 10790 would lose information if truncated down to one byte.
The process of taking one numeric value and converting it to another without your help is called a promotion - You are taking for example a 1 byte number, and making it into a 4 byte number by filling in zeroes. This is (generally) safe since the range of possible values in the target type is greater than in the source type.
When the opposite occurs, such as taking an int and converting to a byte, it is a "demotion" ad it is not automatic, you have to force it since there's a loss of value.
What happens here is that 27 is interpreted as an int. The expression itself is an int, and then it gets sent to a method that expects a byte. In other words, the fact that you are putting it inside a call to a method that takes a byte doesn't change the fact that Java considers it to be a bit to begin with.
I don't remember exactly how to define byte constants in Java, but generally speaking, a number like 27 is a magic number. You may want to write a
final byte MY_CONSTANT_THAT_MEANS_27_BUT_WITH_MEANINGFUL_NAME = 27;
And then call your function with that constant instead of the 27.
By default, Java will look at a numeric value such as 27 as an integer type. Think of this in a similar aspect, if you say 123.45, it will see it as a float, and "hello" is a string. These are static values, and not variables, so you need to cast it to a byte, like so:
a.method((byte)27);

Categories