Java primitive type comparison – A wat look

Not long ago, we had a not too nice issue related to mismatch between int and long variables. As the concrete variables were coming from 1) a model created by others and 2) another model created by us, the solution did not appear to be simple.

We were thinking, if we could compare the variables by value consistently without the concrete type equality, we would not need to support both int and long numbers in our model, so we asked our collegue, Gábor Bergmann to experiment a bit with the comparison.

Sadly, the result is that there is no reasonable way to compare longs and integers by value. However, we found a truly nice wat moment, almost fitting to the issues presented by Gary Bernhardt in a talk called WAT at CodeMash 2012 (or instead of the video, you could have a look at http://www.shopify.com/technology/5370262-wat-a-funny-look-at-ruby-and-javascript-oddities).

Because of Java is statically typed, most inconsistencies of Javascript do not apply here. However, the implicit contracts of Java are sometimes broken, just as the following code snippet shows (it is Gábor’s work entirely, I only changed only the white spaces):

Basically, when comparing primitive types and their boxed values, if you have two different types, all hell’s might break loose. Sometimes two values are considered equal when using ‘==’ but not when using Object.equals. Even worse, transitivity can also be broken.

I cannot think about any possible reason for this behavior, but we could reproduce it issue consistently across different computers using Java 1.6, so this appears to be according to be designed that way.

If someone could provide some reasonable explanation, I would be glad for it. Then we could learn something nice instead of just laughing a bit. Otherwise, be careful with primitive comparison…

Author: Zoltán Ujhelyi

I am an Eclipse Technology Expert at IncQuery Labs Ltd. and a regular contributor of open-source projects, most importantly the VIATRA project at eclipse.org. Furthermore, I am in the process of finishing my PhD in computer science at the Budapest University of Technology and Economics focusing on analysis techniques for model queries and transformations.

12 thoughts on “Java primitive type comparison – A wat look”

  1. System.out.println(new Long(1).equals(1));
    // false, it only equals 1L – so much for semantic equivalence
    is clear because autoboxing makes the 1 to Integer.valueOf(1) and the equals method of java.lang.Object only accepts an Object and if you use
    System.out.println(new Long(1).equals(1L)); you tell the compiler to use Long.valueOf(1L) instead of Integer.valueOf(1)

    System.out.println(new Integer(1).equals(new Long(1)));
    // false… Y U NO EQUAL?
    is also clear just have a look at the javaDoc of java.langObject#equals(Object) (http://docs.oracle.com/javase/6/docs/api/java/lang/Object.html#equals%28java.lang.Object%29)

    System.out.println(new Long(1) == 1);
    this works also because the compiler makes the following code ouf of your code:
    System.out.println(new Long(1L).longValue() == 1L);
    and longValue() is the autoboxing method of a Long object to a long datatype

    and everytime you use the == it compares the references not the content

  2. @WhoCares thanks for your evaluation. First of all, I can understand the non-equality when using equals – although it is not what I would expect from it, but it is acceptable.

    The interesting stuff is when you use ‘==’. The automatic conversion between 1 and 1L in the example is something I do not expect in Java. In Javascript, PHP, etc. it is ok, but in Java that can be quite surprising…

  3. Just think about it what is easier and faster in the situation of comparing primitive datatypes? Checking two primitive datatypes which could afterwards be done by simple machine instruction or comparing two references of objects which could probably differ.

    A conversion of
    System.out.println(new Long(1) == 1);
    to
    System.out.println(new Long(1) .equals( new Long(1) );

    would cause the creation of another Long object; a equals method call and some checks inside of the equals method.

    The call of (new Long(1L).longValue() == 1L) which the compiler produces just calls one method and afterwards a simple compare of two registry/memory entries which is much faster. (BTW if this method is called multiple times maybe in a loop the HotSpot JIT compiler will have a chance to optimize this to a more efficent code without a method call.)

  4. @WhoCares thanks for linking to the javaDoc, but can you point out how you think it explains the behaviour of equals() in this case? new Long(1).equals(new Integer(1)) would not necessarily violate any of the axioms (reflexivity, transitivity, etc.). So the question remains: why did they choose an implementation where equals() returns false in this case? See David Orme’s blog post above for a much more articulate explanation why this implementation has to be considered broken 🙂

    “this works also because the compiler makes the following code ouf of your code” – of course. But the real question is: _why_ does the compiler make 1L out of the right-hand-side, instead of e.g. raising a compile error, such as when one tries to compare Integer with Long?

  5. @WhoCares Sorry, I was not clear. I could understand that System.out.println(new Long(1) == 1) returns false, because the second argument is not a long. Similarly, I could understand that it returns true, because the values are the same.

    Similarly, I could understand the same things for equals as well, but I would like a consistent output, that maintains transitivity as well, instead of the current implementation.

  6. @Gábor Bergmann:
    It would violet the symmetry, because new Long(1L).equals(new Integer(1)) would return true and new Integer(1).equals(new Long(1L)) would return false. To solve this you would need a dependency from java.lang.Long to java.lang.Integer and vica versa and this is not good. (for more information have a look at the book: Effective Java 2nd Edition by Joshua Bloch – Item 8: Obey the general contract when overriding equals << this guy can explain this a lot better than me and he also shows you all the pitfalls with a lot of examples)

    @Zoltán Ujhelyi:
    I can't see any inconsistency because this is the default way of unboxing and auto casting.

  7. @WhoCares I begin to see your point, and thank you for showing it.

    I understand that this is the default way of unboxing and autocasting, but there are some basic expectations that become violated because of that: first of all, if ‘o1==o2’, I also expect ‘o1.equals(o2)’ be true as well. So, in other words, if ‘new Long(1) == 1’ would return false, everything would be fine by me.

    Luckily, the issue is not common, but when it comes forth, it needs some time to understand correctly.

  8. Have you ever thought about why you really need those wrapper classes (java.lang.Long, java.lang.Double, …) and way they are called “wrapper classes”?

  9. @WhoCares: of course, I meant a symmetric modification in both classes (I did not mention it, because I assumed it to be trivial). Java was explicitly designed to allow circular dependencies between classes, so I’m afraid your counter-argument does not hold. For an example from the core JRE, see the circular dependency between java.lang.String and java.lang.StringBuilder.

    So, once again, why isn’t there consistency between == and equals()? It is either the implementation of equals() in Integer, Long, etc. that should be fixed, or the == operator between primitives. Preferably, it is the former, because there really is semantical equivalence between 1L and 1, so == should hold as well as equals().

Leave a Reply