importance of ordering of operands in Java's == - java

Sorry to keep asking the basics but I don't understand this simple code and why the first print statement goes through the compiler ok and even prints true, but the second print statement doesn't compile, giving me an "incomparable types" error:
int in1 = 38;
Number Nn1 = in1;
System.out.println(in1 == Nn1);
System.out.println(Nn1 == in1);
I am not expecting this result, I thought it was pretty standard that == was symmetric?
I am using javac 1.6.0_26 and also NetBeans but get the same result, the first println statement compiles without problem and the second does not..

I believe that, according to the Java Language Specification, neither way round should compile.
It's important firstly to understand that auto(un)boxing is only applied to expressions that meet certain criteria, and only for specific wrapper classes (Integer, Long etc, not Number).
Now, in the case of ==, autounboxing is applied specifically when one is of
[primitive] numeric type and the other is convertible to [primitive] numeric type (JLS 15.12.1) according to the rules. And as we've just stated, "according to the rules", Number is not convertible to a numeric primitive type.
It is NOT, the case, for example, that the int should be converted to an Integer and then a reference comparison made: autoboxing is not specified to be applied to an == reference comparison (JLS 15.21.3).
So if your compiler is allowing the cited code to compile, it does not obey the Java Language Specification.
This behaviour makes sense because to perform a numeric comparison, the compiler needs to know the actual specific type of both operands in order to perform numeric promotion. You might think that you can compare, say, a Number with an integer, and that the compiler should just call .intValue() on the Number. But this is inappropriate, because if the original number type was actually a Float, then the correct comparison is actually to first convert the integer to a Float rather than the other way round. In other words, with a Number, the compiler doesn't have all the information to correctly perform a numeric comparison with a primitive.

My compiler (jdk1.7.0_03 on Windows) says that both lines are incorrect:
Operator == cannot be applied to int and java.lang.Number

When you check for equality between an int and an Integer, unboxing occurs.
In fact, compiler is aware that Integer operand wraps only int. It's like a clue.
However, Number, although implemented by Integer (and others), is too generic and would expect too much job for compiler to extract the original primitive type in order to operate the unboxing.
Hence, compiler complains about it and expects you a more fine-grained type.

Both lines are a compile error. If not, there's a bug in NetBeans.
If you change Number to Integer, both lines compile.
int in1 = 38;
Integer Nn1 = in1; // Changed to Integer
System.out.println(in1 == Nn1); // compiles
System.out.println(Nn1 == in1); // compiles

Because you are comparing values reference-type values with primitive values, the only way it could work would be because of an auto unboxed conversion. But that type of conversion doesn't seem to be specified in the Java Language Specification.
It probably not symmetric because it's not intended to be possible at all. Maybe a compiler bug.

Related

Why can I assign an integer literal to a short type variable but not to a short type method parameter?

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)

short assigned (final int) works but passing (final int) as parameter to short argument doesnt work [duplicate]

Why this is giving the compile time error?
2 is constant at compile time, therefore narrowing should be allowed here since 2 is in range of byte.
public class Test {
public static void main(String[] args) {
ForTest test=new ForTest();
test.sum(1, 2); //compile time error here
}
}
class ForTest
{
public int sum(int a,byte b)
{
System.out.println("method byte");
return a+b;
}
}
The error is:
The method sum(int,byte) in the type ForTest is not applicable for the arguements (int,int).
Edit: I think the answer lies here: http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.3 but I am not getting it :(
You have to distinguish between assignment conversion and method invocation conversion.
Narrowing Primitive Conversion
First of all, look at JLS §5.1.3:
22 specific conversions on primitive types are called the narrowing primitive conversions:
[...]
int to byte, short, or char
[...]
Note, that this only explains the mechanism but not the places where such conversions are allowed or not.
Assignment Conversion
Next, look at JLS §5.2:
[...]
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.
[...]
This clearly describes that in the assignment byte b = 2 the narrowing conversion from type int to type byte is allowed.
Method Invocation Conversion
However, when reading JLS §5.3 you will not read anything about a narrowing conversion. So the compiler is doing a correct job.
This is because 2 is interpreted as an int, which cannot be implicitly converted into a byte.
Your choices are either to change the method signature to (int a, int b), or to explicitly do a casting: test.sum(1, (byte)2)
You can't type cast int to byte implicitly.
Call method as:
sum(1, (byte)2);
Revised answer:
The java language specification says that when looking for candidate matches for function calls, after the candidates have been found based upon the name of the function, and the number of actual vs. formal parameters, it eventually arrives at phase 2.
"If m is not a generic method, then m is applicable by loose invocation if, for 1 ≤ i ≤ n, either ei is compatible in a loose invocation context with Fi or ei is not pertinent to applicability."
Evidently, when it arrive at the second actual parameter, it is deciding that an 'int' is not compatible in a 'loose invocation context' with 'byte'. Therefore it rejects the one and only matching candidate it found in phase 1.
I'm unable to find a formal definition of "loose invocation" in the JLS.
Thanks to VikasMangal and KisHanarsecHaGajjar for pointing out the stupidity of my original answer which is repeated below for eternal shame.
My original answers the comments refer to is below.
Java spec section 5.1.3 says you should be able to do this.
Additionally in section 5.2 on assignment of constants it says
"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."
However, the compiler seems to be going it's own way.
Since Narrowing Primitive Conversions(§5.1.3) says that narrowing of int is done into byte, short, or char. So you need to cast the int to byte where you calling the method. Like this :
test.sum(1, (byte)2);
You can achieve more with the primitive integer. Thus, everytime you write a value that can be an "integer" value, Java automatically wants to cast and thus treat it like an integer.
The converse of this is what happens when you try what you tried here. Java sees a value which can fit as an integer but for it to treat it as a byte, you need to explicitly tell. It's a bit like saying, OK, I know what I am doing and I do realize that casting a number to a byte has its limitations but I want you to do it anyway.

overloaded method call ambiguity with ternary operator

I am creating a simple wrapper class for numbers. Simply put, I want it to display the value 42 verses 42.0; however, it should display the value 1.6180338 as that number. Simply enough.
Code
private double number;
...
#Override
public String toString() {
return String.valueOf(
number == longValue()
? longValue()
: number );
}
...
#Override
public long longValue() {
return (long) number;
}
Issue
The problem is that the value of 42.0 is always displayed Not the correct 42 value in the toString(...) method
My Thoughts
Although the String.valueOf(...) method has a lot of overloaded methods to display the correct primitive values as strings, there is ambiguity in which overloaded method to use. It can use String.valueOf(double) or String.valueOf(long). This is because of the ternary operator statement and resulting result type.
I thought that the compiler would be able to discern the long type and call the appropriate String.valueOf(long) method. That appears to not be the case; instead, the JVM will choose at compile time the safest, yet most-confined overloaded method. In this case, that is String.valueOf(double) because it can safely convert a long to a double.
Question
I know this isn't possible in Java right now, but is something like this available in other languages currently? And is there some kind of definition that explains this method, and can you explain it in more detail?
I mean a definition like Covariance or Contra-variance. Note: I realize that the definition is not one of those two :)
As Java is statically typed language, the result of the ternary operator must have an explicit type, defined during the compilation, so the compilator can continue handling the outer expression. As the both branches of ternary are numbers, they are promoted to the more precise type as described in JLS 15.25 and 5.6.2. You can work-around this casting the arguments to the object:
return String.valueOf(
number == longValue()
? (Object)longValue()
: (Object)number );
This way you will box the numbers and use String.valueOf(Object) which works nice for both branches.

Unboxing Long in java

In some code I see this:
private void compute(Long a, Long b, Long c) {
long result = a-(b+c);
...
It seems a bit strange that the result is stored in a primitive long instead of a Long object corresponding to its operands.
Are there any reason that a result should be stored as a primitive?
It seems a bit strange that the result is stored in a primitive long instead of a Long object corresponding to its operands.
No, what is "strange" is that you can use the + and - operators on Long objects. Before Java 5, this would have been a syntax error. Then autoboxing/unboxing was introduced. What you're seeing in this code is autounboxing: the operators require primtives, so the compiler automatically inserts a call to longValue() on the objects. The arithmetic is then performed on primitive long values, and the result is also a long that can be stored without further conversion on the variable.
As for why the code does this, the real question is why someone would use the Long type instead of long. Possible reasons:
The values come from some library/API that delivers Long values.
The values are stored in collections (List, Map), which cannot hold primitives.
Sloppiness or cargo cult programming.
The ability to have null values is required, e.g. to signal unavailable or uninitialized data.
Note that the ability of Long to hold null values means that the calculation (or more specifically, the longValue() calls inserted by the compiler) can fail with a NullPointerException - a possibility the code should deal with somehow.
The reason is obvious: result is declared as primitive.
The arithmetic operators + and - are not defined for boxed types (e.g. Long) but for primitive types (e.g. long).
The result is also a long. See Autoboxing and Unboxing tutorial
Autoboxing this into a Long would result in a small performance cost. It is also unnecessary because
We know it will be non-null (if a, b or c were null, a NullPointerException would occur).
It would be autoboxed implicitly if we use it later where a Long is required.
Based on your needs.I mean the decelaration.
Autoboxing and unboxing can happen anywhere where an object is expected and primitive type is available
Usually you should prefer using primitives, especially if you are certain they cannot be null. If you insist on using the boxed types always think extra hard about what happens when it is null.
Java will do the boxing and unboxing automatically for you, but staring at an int and wondering why you got a NullPointerException can be fun.
From Java 1.5 onwards, autoboxing and unboxing occurs implicitly whenever needed.
The following line:
long result = a-(b+c);
...asks Java to take the result of the expression using 3 Longs, and then store it in a primitive long. Before Java 5, it would complain about the types not matching - but these days it just assumes you mean what you say and automatically does the conversion from object to primitive type for you.
In this example however, unless there's some other good reason not presented here, there's absolutely no point having the parameters as the boxed, object type in the first place.
As per the javadoc
Boxing conversion converts expressions of primitive
type to corresponding expressions of reference type.
Specifically, the following nine conversions are called the boxing conversions:
From type boolean to type Boolean
From type byte to type Byte
From type short to type Short
From type char to type Character
From type int to type Integer
From type long to type Long
From type float to type Float
From type double to type Double
From the null type to the null type
Ideally, boxing a given primitive value p, would always yield an identical reference.
In practice, this may not be feasible using existing implementation techniques. The
rules above are a pragmatic compromise. The final clause above requires that certain
common values always be boxed into indistinguishable objects. The implementation may
cache these, lazily or eagerly. For other values, this formulation disallows any
assumptions about the identity of the boxed values on the programmer's part. This would
allow (but not require) sharing of some or all of these references.
This ensures that in most common cases, the behavior will be the desired one, without
imposing an undue performance penalty, especially on small devices. Less memory-limited
implementations might, for example, cache all char and short values, as well as int and
long values in the range of -32K to +32K.`
Here is the Oracle Doc source
The answer for your doubt is
autoboxing and autounboxing in Java which converts from primitive to wrapper class objects and vice versa respectively.
autoboxing means internally compiler uses valueOf() method of primitive classes ans autounboxing means internally compiler uses xxxValue() method.
Suppose for
private void compute(Long a, Long b, Long c) {
long result = a-(b+c);
its makes this conversion a.longValue()-(b.longValue()+c.longValue())
Which means even before your statement performs addition the compiler provides the primitives of long type as input to your operands
Remember that this goes in hand as JAVA is statically and strongly typed language.
Hence you get long type output
I hope i cleared your doubt

Java Integer vs. String Autoboxing Design

In Java, I can do the following to succinctly guard against a NullPointerException:
if ("myString".equals(someOtherString))
But I cannot do the same with Integers, e.g.
if (5.equals(someOtherInteger))
I get a compile-time error. Any ideas on why this design decision was made? Or any resources that might explain it? Thanks in advance.
EDIT: someOtherInteger is an Integer, not an int.
String has always been an object in Java. There is no autoboxing for strings, and there can't be in principle. Autoboxing from the primitive int to the Integer object has been introduced fairly recently.
It is valid to ask why trying to access member variables of primitives doesn't invoke autoboxing (95.toString(radix) would actually be pretty convenient), but I imagine that the reason is that it wasn't considered a likely use-case, since since almost every wrappedPrimitive.method() has an equivalent WrapperClass.method( primitive ) version.
equals() is usually unnecessary for primitive types since == is already there. However, you do make a good case for it as a null-guard... 5 == integerInstance will try to unbox the instance, and throw a NullPointerException if the instance is null, unfortunately. (I didn't fully appreciate your point at first.)
That said, it would be really cool if we could hear from someone working on Java either currently or at the introduction of autoboxing about whether they considered this sort of functionality.
The JLS specifies that boxing conversions can only occur during assignment conversions, method invocation conversions, or casting conversions. Since you are neither assigning 5 to a variable, passing it as an argument to a method, nor explicitly casting it to Integer, it will not be autoboxed for you.
Assignment conversion (§5.2, §15.26) converts the type of an
expression to the type of a specified variable.
Assignment conversion may cause an OutOfMemoryError (as a result of
boxing conversion (§5.1.7)), a NullPointerException (as a result of
unboxing conversion (§5.1.8)), or a ClassCastException (as a result of
an unchecked conversion (§5.1.9)) to be thrown at run-time.
Method invocation conversion (§5.3, §15.9, §15.12) is applied to each
argument in a method or constructor invocation and, except in one
case, performs the same conversions that assignment conversion does.
Method invocation conversion may cause an OutOfMemoryError (as a
result of boxing conversion (§5.1.7)), a NullPointerException (as a
result of unboxing conversion (§5.1.8)), or a ClassCastException (as a
result of an unchecked conversion (§5.1.9)) to be thrown at run-time.
Casting contexts allow the use of one of:
...
a boxing conversion (§5.1.7) optionally followed by a widening
reference conversion (§5.1.5)
you can use
if (someOtherInteger!=null && someOtherInteger == 5)
I suspect that autoboxing is not implemented for the literal 5, whereas it is for a string myString, as a safety measure. It's safe to autobox a syntactic structure that is prepended and appended with double quotation marks "", because it's unlikely that the quotation marks are unintended, so the user's intention is clear and type-safety is not compromised.
However, the literal 5 could be a typo on the user's part - or it could be intended to be a string, rather than an integer. Therefore, to maintain the benefit that variables must be declared before use in object-oriented programming in order to prevent typos (among many other advantages) (even if it's implicit, as in the case of autoboxing), 5 is not autoboxed.
Here is a bit of reading on the different comparisons:
http://www.leepoint.net/notes-java/data/expressions/22compareobjects.html
Not sure if it was a built in design to reject int
If you do
Integer s=5;
Integer d=5;
if(d.equals(s)){
System.out.println("Fun");
}
It works just fine.
int is a primitive type it doesn't support any methods itself. To compare 2 ints you simply use the == convention as in:
if(a == b)
There is an Integer class that is a wrapper for an int that supports some other method calls
Edit:
Based on your edit you want to compare to Integer but the problem is the literal 5 isn't an Integer you have to create a new integer for it.
Integer myInt = 5;
if(myInt.equals(someOtherInteger)) ...
This design is inherent in the fact that primitives don't have any methods. As to whether primitives should support methods (or simply not exist) is integral to the debate as to whether Java is a Pure Object Oriented Language or not (many say no due to the fact that primitives exist).

Categories

Resources