Integer Enum and Unboxing - java

public class Document{
private Integer status;
// get()/set()
}
Then an enum:
public enum DocumentStatusEnum {
ACTIVE_DOCUMENT(2060),CANCELLED_DOCUMENT(2061),DRAFT_DOCUMENT(2062),PROCESSED_DOCUMENT(2063);
private final Integer value;
private DocumentStatusEnum(Integer value){
this.value = value;
}
public Integer getValue(){
return value;
}
}
In a method I'm using the above method as below:
Document d = new Document();
d.setStatus(2063);
if (d.getStatus() == DocumentStatusEnum.PROCESSED_DOCUMENT.getValue()){
{
// print true;
}
else{
// print false;
}
I get true here. Looks alright. In the same method, After couple of lines, I do this:
d.setStatus(2060)
if (d.getStatus() == DocumentStatusEnum.ACTIVE_DOCUMENT.getValue()){
// print true
}
else{
// print false
}
I get a false. I researched and found something about caching and boxing features in Java. I converted the enum definition as this:
public enum DocumentStatusEnum {
ACTIVE_DOCUMENT(2060),CANCELLED_DOCUMENT(2061),DRAFT_DOCUMENT(2062),PROCESSED_DOCUMENT(2063);
private final int value;
private DocumentStatusEnum(int value){
this.value = value;
}
public int getValue(){
return value;
}
}
Now, no issues. I get true in both cases.
Question is why this happening? I feel this behaviour is extremely unstable. It's a large application I'm dealing with and I'm using Integer == Integer comparison every where. Am I safe here?

Integer extends Object and therefore == is defined as reference equality, not value equality.
When comparing Integer with int, the Integer will be unboxed to an int, and the two operands will be compared for value equality. (Unless the Integer value is null, in which case NullPointerException will be thrown instead.) But Integer == Integer is never safe.
That said, because by default the runtime pre-allocates Integer instances for small integers (-128 through 127, according to the OpenJDK source), you can often get Integer == Integer to work for small values. But that behavior does not hold for larger values, and it is never required to hold. So you should never assume that two instances of Integer (or String, or any Object) will compare equal using ==, unless you are explicitly looking for reference equality.

You should use int instead of Integer, unless you need to handle null values.
There are indeed issues with comparing Integer objects. For example, the following will evaluate to false:
boolean test = (new Integer(1) == new Integer(1));
System.out.println(test); // "false"
These are just objects like any other. The == operator, when used with objects, only evaluates to true if they are the exact same Java object (as opposed to the equals() method, which can be overridden to compare the internals).

Related

ASM Merge two value objects on Interpreter

I continue to research method emulation and getting actual value when passing instruction ILOAD. After Holger's help with Interpreter
and after adding new operations with local variable in main() method I stucked with merge(V a, V b) method, which must be overriden when extending Interpreter.
#Override
public LocalValue merge(LocalValue valueA, LocalValue valueB) {
if (Objects.equals(valueA, valueB)) return valueA;
else return new LocalValue(basicInterpreter.merge(valueA.type, valueB.type), null);
}
But it seems this not correctly written. I can try different logic vars what to return but without understanding, in what cases values can merge, I can't find that. There is no useful info I tried to find in javadocs and asm-4 tutorial. So, what I need to return, when:
- One value is null, and other is not
- Both values are not null, same type, but different objects (such as 0 and 5)
- Both values are not null, different types
basicInterpreter:
private BasicInterpreter basicInterpreter = new BasicInterpreter();
LocalValue:
public static class LocalValue implements Value {
Object value;
BasicValue type;
public LocalValue(BasicValue type, Object value) {
this.value = value;
this.type = type;
}
#Override public int getSize() {return type.getSize();}
#Override public String toString() {return value == null ? "null" : value.toString();}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof LocalValue)) return false;
LocalValue otherV = (LocalValue) obj;
return Objects.equals(otherV.type, type) && Objects.equals(otherV.value, value);
}
}
Values need to be merged when an instruction can be reached through different code paths, e.g when you have conditionals, loops or exception handlers.
So when the value is the same, regardless of which code path has been taken, you can keep it, otherwise the value is not a predictable constant anymore. So in my code, where null has been used to denote unknown values, it always returns null when the values differ.
So when you have code like
void foo(int arg) {
int i = 1;
int j = arg%2==0? i: arg;
}
The values for arg, i, and the value on the operand stack get merged right before the assignment to j. arg does already have an unpredictable value, i has the value 1 in each code path, but the value on the operand stack, to be assigned to j has different values, 1 or “unknown”, depending on which code path has been taken.
You may decide to maintain a set of possible value, if you like, but when one of the possible values is “unknown”, the result of the merging cold be any value, hence, is “unknown”.

Array.equal() giving wrong output

To my understanding, the following code should print true, since both elements are equal.
From java docs Array.get() will return:
Returns the value of the indexed component in the specified array
object. The value is automatically wrapped in an object if it has a
primitive type.
However, when I run the following code it is printing
false:
public class Test1 {
static boolean equalTest(Object array1, Object array2) {
return Array.get(array1, 0).equals(Array.get(array2, 0));
}
public static void main(String[] args) {
int[] a = new int[1];
byte[] b = new byte[1];
a[0] = 3;
b[0] = 3;
System.out.println(equalTest(a, b));
}
}
My question is isn't classes implementing Number are or should be directly comparable to one another.
This has nothing to do with arrays really. Your comparison is equivalent to:
Object x = Integer.valueOf(3);
Object y = Byte.valueOf((byte) 3);
boolean equal = x.equals(y);
That's never going to return true.
Even though your original arrays are of the primitive types, Array.get returns Object, so you're getting the boxed types - and comparing values of those different types.
According to the documentation of Array.get(Object array,int index) method, the value returned is automatically wrapped in an object if it has a primitive type. So, if you add the following lines:
System.out.println(Array.get(array1, 0).getClass());
System.out.println(Array.get(array2, 0).getClass());
you will see the output is
class java.lang.Integer
class java.lang.Byte
The equals method of Integer class first of all checks if the object it is being compared to is also an instance of Integer, if not , then no further checks are required, they are not equal.
That's the reason you see the output is false as the objects being compared for equality are Integer and Byte.
The Array.get invocations return Integer and Byte object instances. These are not equal according to Integer.equals because the class type differs.
The problem is that the Array.get() method returns an object, so for int it'll return Integer, and for byte, it will return Byte. So .equals() method will return false because it first sees whether the type is the same, and then compares the values.
The array data types don't match. One is int and the other is byte. Since they're being passed to the function as Objects, they'll end up being Integer and Byte. So in order to compare them properly, you need to type-cast them to the same type. Something like:
static boolean equalTest(Object array1, Object array2) {
int int1 = (int) Array.get(array1, 0);
int int2 = (int) Array.get(array2, 0);
return int1.equals(int2);
// OR return int1 == int2;
}
If you look at the JavaDoc for Array.get you'll see:
The value is automatically wrapped in an object if it has a primitive
type.
So your bytes become Bytes and your ints become Integers.
That means the function you're calling is Integer.equals(Object)
This is implemented like so:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
You're not passing it an Integer, you're passing it a Byte so that's why it returns false.

Why == in case of Integer check for value not reference in java , case different with new operator

As we know that == in case of Objects returns true if pointing to the same reference else it returns false.
So , if i have taken
Integer a = new Integer("1"); // Creating Integer Object a
Integer b = new Integer("1"); // Creating Integer Object b
and then perform a == b , then it returns true
but they both have different references.
The JVM caches integer values between -127 to 127.
That's the reason == works for Integer value between this range.
But:
Integer i1=new Integer("11");
Integer i2=new Integer("11");
System.out.println(i1==i2); //false
Integer i3=11;
Integer i4=11;
System.out.println(i3==i4); //true
Integer i5=128;
Integer i6=128;
System.out.println(i5==i6); //false
Because it is overridden. source:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
Let me more elaborate on this, #asAmit Bhati said in his answer when we write
Integer i1=new Integer("11");
Integer i2=new Integer("11");
System.out.println(i1==i2); //false
jvm creates two separate objects and comparing them with "==" results in false. but when we write the following code:
Integer i3=11;
Integer i4=11;
System.out.println(i3==i4); //true
it will be translated into this:
Integer i3=Integer.valueOf(11);
Integer i4=Integer.valueOf(11);
Implementation of valueOf method is as follow(in java 1.8):
public static Integer valueOf(int var0) {
return var0 >= -128 && var0 <= Integer.IntegerCache.high?Integer.IntegerCache.cache[var0 + 128]:new Integer(var0);
}
as you can see if the value is between -128 and Maximum cache value (which can be configured using this jvm parameter -Djava.lang.Integer.IntegerCache.high), it will retrieve the cached value and doesn't create a new instance of Integer that is why (==) returns true for certain values!
also note that the same goes for Character Wrapper class but not for Float and Double classes.
As the answer above says, it may work in many cases, nevertheless you shouldn't compare two Integer with == since it may present problems in some cases.
Check this answer for more information:
Comparing Integer values in Java, strange behavior
If you check equals method implementation in Integer class it is:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
From here you can see it uses "==" operator.
Now the reason behind it. Ultimately you have to compare value of the Integer wrapper class, which will happen automatically because of autoboxing and unboxing in Java.
And also from the method definition you can see that it is retrieving passed Object value using ((Integer)obj).intValue().

Why isn't Integer.MIN_VALUE equal to stored Integer.MIN_VALUE in variable?

I made an Interval class with the following fields:
...
private static final Integer MINF = Integer.MIN_VALUE;
Integer head,tail;
...
when I make an instance of this class, making this.head = Integer.MIN_VALUE, and I want to check if the value of head is equal to MINF, it says that they aren't equal.
Interval i = new Interval(Integer.MIN_VALUE,10);
System.out.println(i.toString()); //[-2147483648,10]
So I went ahead and tried to print the values,
public String toString() {
...
//What the hell?
System.out.println("MINF == Integer.MIN_VALUE: " + (MINF == Integer.MIN_VALUE)); //true
System.out.println("MINF == this.head: " + (MINF == this.head)); //false
System.out.println("Integer.MIN_VALUE == this.head: " + (Integer.MIN_VALUE == this.head)); //true
...
return "*insert interval in format*";
}
Which says
MINF == Integer.MIN_VALUE is true
MINF == this.head is false, although this.head = -2147483648
Integer.MIN_VALUE == this.head is true
Am I missing something for why the second one is false?
Integer is the wrapping class, child of Object and containing an int value.
If you use only the primitive type int, == does a numerical comparison and not an object address comparison.
Mind that Integer.MIN_VALUE of course is an int too.
You are missing the fact that when stored in Integer (that is, you store Integer.MIN_VALUE in two different integers) and using == between them, the comparison is not of the values, but of the objects.
The objects are not identical because they are two different objects.
When each object is compared to Integer.MIN_VALUE, since Integer.MIN_VALUE is an int, the object is autounboxed and compared using int comparison.
No one here has addressed the REASON why they're different objects. Obviously:
System.out.println(new Integer(10) == new Integer(10));
outputs false, for reasons that have been discussed to death in the other answers to this question and in Comparing Integer objects
But, why is that happening here? You don't appear to be calling new Integer. The reason is that:
Integer.MIN_VALUE returns an int, not an Integer.
You have defined MINF to be an Integer
Autoboxing uses valueOf. See Does autoboxing call valueOf()?
valueOf calls new Integer if the int is not in the integer cache,
The cache is only the values -128 -> 127 inclusive.
And that is why you are seeing the "two Integer objects are not == behavior", because of autoboxing. Autoboxing is also why equality does not appear to be transitive here.
You can fix this problem by instead using:
private static final int MINF = Integer.MIN_VALUE;
And, in general: don't use Integer for simple fields.; only use it as a generic type where you actually need the object.
You are using Integer objects. The use of == should be used as a comparison of individual primitive's values only. Since you used the Integer class rather than the primitive int then it is comparing the object's references between the two variables rather than their values.
Because MINF is a separate object to head you are receiving false for a direct comparison using ==.

How to compare Integer correctly in Java [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Integer wrapper class and == operator - where is behavior specified?
I known Java integer use cache in -127~128.
If
Integer i = 1;
Integer j = 1;
Integer m = 128;
Integer n = 128;
i == j // true
m == n // false
But I met a strange phenomenon.First,look at following snippet.
List<CustomerNotice> customerNotice = findByExample(example); // use Hibernate findByExample method
for(CustomerNotice n : customerNotice){
if(n.getConfirmStatus() == NoticeConfirmStatus.UNCONFIRMED.getValue()){
// do sth
}
}
public enum NoticeConfirmStatus{
UNCONFIRMED(1), //
CONFIRMED(2), //
FAILED_TO_CONFIRM(3); //
private final Integer value;
private NoticeConfirmStatus(Integer value) {
this.value = value;
}
public Integer getValue() {
return this.value;
}
}
public class CustomerNotice {
#Column(name = "CONFIRM_STATUS")
private Integer confirmStatus;
public Integer getConfirmStatus() {
return this.confirmStatus;
}
public void setConfirmStatus(Integer confirmStatus) {
this.confirmStatus = confirmStatus;
}
}
Although the if expression is not recommended, I think it will be return true,because n.getConfirmStatus()==1, but the result is false.I'm very confusing.
In addition, theList<CustomerNotice> customerNotice acquired by Hibernate findByExample method. Is there some Autoboxing or new operation when retrieve the resultset?
Thank you.
SHORT: (answers question)
If you want to compare Integers as the objects, you should use .equals:
i.equals(j);
m.equals(n);
With this, they should both return true. But if you really want to use ==, you need to get the primitive int value:
i.intValue() == j.intValue();
m.intValue() == j.intValue();
LONG: (explains answer)
The basis of this is that Objects are always stored separately in memory (except for some special cases like m=n), and to be compared properly, they need to be broken down into primitive types that can be compared successfully using ==.
Every Object has a .equals() method, which is inherited from Object as its superclass. However, it must be overridden to do a proper comparison. Integer overrides this method to compare to Integer objects successfully, while using == checks to see if both objects point to the same space in memory, and because two instances of an Object cannot point to the same space in memory, this will always return false.
However, as your code points out, there are some special cases that work, like these:
Your code uses a Integer i = 1, which is considered a "standard instance" and is able to be compared using ==.
If you set one Object equal to another using =, Java tells both objects to point to the same location in memory, which means that == will return true.
There are many others, but those are the two that come to mind and seem relevant.
You'll drive yourself crazy and waste a lot of time trying to figure out specific cases where this works or does not work. It depends on the implementation of code which isn't always visible to you.
The bottom line: never, ever, use == to compare Integer instances, period. As you have seen, it works sometimes, under some circumstances, and fails miserably the rest of the time. If you have a method that returns an Integer, then assign the value to an int, and then you can use == to compare that int to another int.

Categories