The values of boxed primitives cannot be directly compared using the ==
and !=
operators because these operators compare object references , rather than object values. Programmers could can find this behavior surprising because autoboxing memoizes, or caches, the values of some primitive variables. Consequently, reference comparisons and value comparisons produce identical results for the subset of values that are memoized.
Wiki Markup |
---|
Autoboxing automatically wraps a value of a primitive type with the corresponding wrapper object. The _Java Language Specification_ (JLS) [§5.1.7, "Boxing Conversion,"|http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.7] of the _Java Language Specification_ \[[JLS 2005|AA. Bibliography#JLS 05]\], explains which primitive values are memoized during autoboxing: |
...
Use of the ==
and !=
operators for comparing the values of boxed primitive types that are not fully memoized is permitted only when the range of values represented is guaranteed to be within the ranges specified by the Java Language Specification JLS to be fully memoized.
Use of the ==
and !=
operators for comparing the values of boxed primitive types is not allowed in all other cases.
...
Less memory-limited implementations could, for example, cache all characters and shorts, as well as integers and longs in the range of -32K — to +32K.
Code that depends on implementation-defined behavior is non-portable.
...
The Integer
class is only guaranteed to cache integer values from -128
to 127
, which can result in equivalent values outside this range comparing as unequal when tested using the equality operators. For example, a Java Virtual Machine (JVM) that did not cache any other values when running this program would output
...
This noncompliant code example attempts to count the number of indices in arrays list1
and list2
that have equivalent values. Recall that class Integer
is required to memoize only those integer values in the range -128 to 127; it might return a non-unique nonunique object for any value outside that range. Consequently, when comparing autoboxed integer values outside that range, the ==
operator might return false
, and the output of this example might be example could deceptively output 0.
Code Block | ||
---|---|---|
| ||
public class Wrapper { public static void main(String[] args) { // Create an array list of integers, where each element // is greater than 127 ArrayList<Integer> list1 = new ArrayList<Integer>(); for (int i = 0; i < 10; i++) { list1.add(i + 1000); } // Create another array list of integers, where each element // has the same value as the first list ArrayList<Integer> list2 = new ArrayList<Integer>(); for (int i = 0; i < 10; i++) { list2.add(i + 1000); } // Count matching values. int counter = 0; for (int i = 0; i < 10; i++) { if (list1.get(i) == list2.get(i)) { // uses '==' counter++; } } // printPrint the counter: 0 in this example System.out.println(counter); } } |
If However, if the particular JVM running this code memoized integer values from -32,768 to 32,767, all of the int
values in the example would have been autoboxed to singleton Integer
objects, and the example code would have operated as expected. Using reference equality instead of object equality requires that all values encountered fall within the interval of values memoized by the JVM. The Java Language Specification JLS lacks a specification of this interval; rather, it specifies a minimum range that must be memoized. Consequently, successful prediction of this program's behavior would require implementation-specific details of the JVM.
...
Code Block | ||
---|---|---|
| ||
public class Wrapper { public static void main(String[] args) { // Create an array list of integers, where each element // is greater than 127 ArrayList<Integer> list1 = new ArrayList<Integer>(); for (int i = 0; i < 10; i++) { list1.add(i + 1000); } // Create another array list of integers, where each element // has the same value as the first one ArrayList<Integer> list2 = new ArrayList<Integer>(); for (int i = 0; i < 10; i++) { list2.add(i + 1000); } // Count matching values int counter = 0; for (int i = 0; i < 10; i++) { if (list1.get(i).equals(list2.get(i))) { // uses 'equals()' counter++; } } // printPrint the counter: 10 in this example System.out.println(counter); } } |
...
Code Block | ||
---|---|---|
| ||
public void exampleEqualOperator(){ Boolean b1 = new Boolean("true"); Boolean b2 = new Boolean("true"); if ( b1 == b2) ){ // never equal System.out.println("Never printed"); } } |
Compliant Solution (new Boolean
)
...
Code Block | ||
---|---|---|
| ||
public void exampleEqualOperator(){ Boolean b1 = true; // Or Boolean.True Boolean b2 = true; // Or Boolean.True if (b1 == b2) { // always equal System.out.println("Will always be printed"); } } |
Exceptions
EXP03-EX0 In the unusual case where a program is guaranteed to execute only on a single implementation, it is permissible to depend upon on implementation-specific ranges of memoized vulnerabilitiesvalues.
Risk Assessment
Using the equivalence operators to compare values of boxed primitives can lead to erroneous comparisons.
...
Related Guidelines
CWE-595, ". Comparison of Object References Instead of Object Contents" object references instead of object contents | |
| CWE-597, ". Use of Wrong Operator in String Comparison" wrong operator in string comparison |
Bibliography
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="5c7878f5b8680575-aaea1f20-489346f7-bd29b37b-cd4f49e5495e353865526daf"><ac:plain-text-body><![CDATA[ | [[Bloch 2009 | AA. Bibliography#Bloch 09]] | 4. ", Searching for the One" | ]]></ac:plain-text-body></ac:structured-macro> | |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="1f332c5685894a52-83194950-4246457d-b02fbf0d-58e82b8c6645fe8417558e22"><ac:plain-text-body><![CDATA[ | [[JLS 2005 | AA. Bibliography#JLS 05]] | [§5.1.7, " Boxing Conversion" | http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.7] | ]]></ac:plain-text-body></ac:structured-macro> |
<ac:structured-macro ac:name="unmigrated-wiki-markup" ac:schema-version="1" ac:macro-id="82ed853948a782f1-b0b703a6-4c8c4da6-905a80a5-52397b96210fc15815c422d4"><ac:plain-text-body><![CDATA[ | [[Pugh 2009 | AA. Bibliography#Pugh 09]] | Using == to compare objects rather than .equals | ]]></ac:plain-text-body></ac:structured-macro> |
...