Does autoboxing call valueOf()?
Asked Answered
R

4

53

I'm trying to determine whether the following statements are guaranteed to be true:

((Boolean)true) == Boolean.TRUE
((Boolean)true) == Boolean.valueOf(true)
((Integer)1) == Integer.valueOf(1)

I've always assumed that autoboxing was equivalent to calling valueOf() on the corresponding type. Every discussion that I've seen on the topic seems to support my assumption. But all I could find in the JLS was the following (§5.1.7):

If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\u0000' and '\u007f' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.

That describes behavior identical similar* to that of valueOf(). But there doesn't seem to be any guarantee that valueOf() is actually invoked, meaning there could theoretically be an implementation that keeps a separate, dedicated cache for autoboxed values. In such a case, there might not be identity equality between cached autoboxed values and regular cached boxed values.

Oracle's autoboxing tutorial states matter-of-factly that li.add(i) is compiled to li.add(Integer.valueOf(i)), where i is an int. But I don't know whether the tutorial should be considered an authoritative source.


*It's a slightly weaker guarantee than valueOf(), as it only refers to literal values.

Rabelais answered 16/7, 2015 at 3:44 Comment(14)
@Jean-FrançoisSavard This is not a duplicate of 408661. In fact I linked to that in my question. I know that it generally compiles to valueOf(); my question is whether the JLS makes any guarantee in that regard.Rabelais
Interesting theoretical question. May I ask where you want to apply this?Henni
This sort of question is hard (but not impossible) to give a definitive answer to, because it (technically speaking) requires you to read the entire JLS back to back and make sure that there are no such guarantees. (I posted a question with the same problem a while ago.) That being said, I searched the entire JLS for valueOf and none of the hits were related to autoboxing (only stuff about Enum.valueOf etc). In my opinion that settles it.Gause
Here's a thought: If I wrote a compiler that did not use valueOf, or if javac switched to another solution, then any new bytecode emitted would be incompatible with old bytecode, since the old bytecode used valueOf for the autoboxing, and two autoboxed values must (at least under some circumstances) be referentially equivalent. Now to tie this up to a formal proof one would have to find something in JLS stating certain guarantees for split compilations. I doubt JLS covers such topics though.Gause
@Gause What if "another solution" also uses a mechanism based on cached values ?Dric
If that other mechanism uses a different set of cached values than the values cached by Integer.valueOf then old autoboxing code wouldn't be compatible with new autoboxing code. (Or rather, the new compiler wouldn't be compatible with the old compiler, despite both adhering to JLS, which seems a bit contradictory.)Gause
@Gause But what if that mechanism use the same set of cached values ? Suppose that for some upcoming features (example Value Types of project Valhalla) some addition need to be done to the autoboxing mechanism which force us to implement an more complex architecture than simply calling "valueOf", but still at the root initially obtain the cached values then we would have autoboxing not using anymore internally valueOf while still beeing compatible with old bytecode (I guess). What I'm trying to point here is that I don't think it is possible to guarantee such feature...Dric
... In fact what we should guarantee here is that as of now, valueOf is the most appropriate method for autoboxing and that there is no reason (at least obviously) to do otherwise.Dric
If that other mechanism used the same set of values, I guess the compilers would still be compatible. But if it used a different set of cached values, the two compilers would not be compatible (even though both adhered to JLS). Obviously using valueOf is the appropriate and best solution, but that's not what this question is about.Gause
@Jean-FrançoisSavard If both implementations use the same cache, it's irrelevant to me whether they actually use valueOf(), since there would be no behavioral difference.Rabelais
@Rabelais I know that, just trying to point that we can't guarantee how it is internally done.Dric
@Gause By your reasoning, any unspecified, observable behavior becomes implicitly part of the JLS definition. That goes contrary to common understanding regarding any specification not to depend on undocumented behavior. It's also technically impossible, since your "rule" can be applied to two compilers with distinct undocumented behaviors, while there can only be one actual specification.Rabelais
To some extent yes... Which is why I posted it as a comment starting with "Here's a thought" :-) To be clear, I don't believe valueOf is required.Gause
@Jean-FrançoisSavard how can the hypothetical future feature “force us to implement an more complex architecture than simply calling ‘valueOf’”, but at the same time provide values compatible to existing classes calling valueOf? If valueOf continues to do the right thing, I see no reason not to call it in a compiled class. Anything more fancy could be injected by the JVM, replacing the call, at runtime. Just like it may already happen today.Stipel
C
30

I first tought your question was a dupe of What code does the compiler generate for autoboxing?

However, after your comment on @ElliottFrisch I realized it was different :

I know the compiler behaves that way. I'm trying to figure out whether that behavior is guaranteed.

For other readers, assume that "behaves that way" means using valueOf.

Remember that there are multiples compilers for Java. To be "legal" they must follow the contract given in the JLS. Therefore, as long as all the rules here are respected, there is no guarantee of how autoboxing is internally implemented.

But I don't see any reason to not use valueOf, specially that it uses the cached values and is the recommended way as per this article by Joseph D. Darcy.

Comeon answered 16/7, 2015 at 3:59 Comment(3)
As long as the compiler produces Java bytecode, not using valueOf would break the interoperability with other classes produced by a compiler using valueOf, especially the entire JRE classes of the reference implementation, as the guaranty that the same object is provided in certain situations must work across classes. This is one of the things being specified in that large gap between JLS and JVMS, which compiler are bound to.Stipel
@Stipel Can you give an example of what could be affected?Rabelais
@Rabelais the JLS implies that when you have a class A with static Integer foo() { return 42; } and a class B with static Integer bar() { return 42; }, then A.foo() == B.bar() and it doesn’t say “only when using the same compiler”. So when one class is compiled with javac, using Integer.valueOf(int), the other class must end up at an Integer also returned by Integer.valueOf(int), regardless of which compiler has been used for B. It could insert additional caching mechanisms, but the origin of the object must be the same to ensure the guaranteed reference equality.Stipel
G
20

Until the language specification mentions it, it is not guaranteed that autoboxing is equivalent to a call to the static valueOf methods. It is an implementation aspect, not part of the boxing conversion specification. An implementation is theoretically free to use another mechanism as long as it conforms to the rule you mentioned from the JLS.

In practice, there are many Sun JDK bug reports (e.g. JDK-4990346 and JDK-6628737) that clearly imply that when autoboxing was introduced in Java 5, the intention was having the compiler to rely on valueOf as stated in JDK-6628737:

The static factory methods Integer.valueOf(int), Long.valueOf(long), etc. were introduced in JDK 5 for javac to implement the caching behavior required by the autoboxing specification.

But that's only for javac, not necessarily all compilers.

Gregory answered 1/8, 2015 at 9:27 Comment(0)
H
4

Autoboxing is absolutely implemented using valueOf() ...in the OpenJDK. If that's your implementation, read on... if not, skip to below.

((Boolean)true) == Boolean.TRUE
((Boolean)true) == Boolean.valueOf(true)

Java documentation states that Boolean.valueOf() always returns Boolean.TRUE or Boolean.FALSE, therefore your reference comparisons in these cases will succeed.

((Integer)1) == Integer.valueOf(1)

For this particular example, under the OpenJDK implementation with default settings, it will probably work by virtue of the fact that you picked a value < 128 which is cached at startup (although this can be overridden as a commandline arg). It may also work for larger values if it's frequently used enough to be cached. Unless you're working under "safe" assumptions about the Integer cache, don't expect the reference comparison to be an equality.

Long, Short, Character and Byte incidentally implement this caching too, but unlike Integer, it's not tunable. Byte will always work if you're comparing autobox/valueOf() references since obviously, you can't go out of range. Float and Double will unsurprisingly always create a new instance.


Now, in purely generic terms? See this section of the JLS - you MUST be given equal references for boolean and any int or char within the -128 to 127 range. There are no guarantees for anything else.

Harumscarum answered 7/8, 2015 at 7:0 Comment(0)
F
-1

Oracle's autoboxing tutorial states matter-of-factly that li.add(i) is compiled to li.add(Integer.valueOf(i)), where i is an int. But I don't know whether the tutorial should be considered an authoritative source.

I'm running Oracle Java 1.7.0_72 it looks like it does use valueOf. Below is some code and the bytecode for it. The bytecode shows it is using valueOf.

public class AutoBoxing {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        Integer x = 5;
        int i = x;
        System.out.println(x.toString());
    }

}





Compiled from "AutoBoxing.java"
public class testing.AutoBoxing {
  public testing.AutoBoxing();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_5
       1: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
       4: astore_1
       5: aload_1
       6: invokevirtual #3                  // Method java/lang/Integer.intValue:()I
       9: istore_2
      10: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
      13: aload_1
      14: invokevirtual #5                  // Method java/lang/Integer.toString:()Ljava/lang/String;
      17: invokevirtual #6                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      20: return

But I do not know what Open JDK uses. Will try it out.

Fowliang answered 31/7, 2015 at 17:36 Comment(9)
How does this answer the question ?Dric
@Jean-FrançoisSavard, I did not find any sentences that ended in question marks in the original post. So I guessed that the last sentence sounded like the question to me. The post is made up of a few assertions and discoveres and ends with But I don't know whether the tutorial should be considered an authoritative source. . So to me that looked like the question worth tackling.Fowliang
@JoseMartinez, the first sentence was the question, despite the lack of a question mark. If I was looking for anecdotal evidence, I could have answered my question by simply executing my own code.Rabelais
I don't quite follow. The JLS does not mention the implementation. Oracle Java mentions how it does it. What is it exactly you are looking for besides repeating those facts back to you? The only other thing you could have been asking for, which it seems to me you did, is proof that Oracle Java does in fact use valueOf().Fowliang
This is for sure the best answer. If you wonder whether it uses valueOf, here's the code, see it for yourself. Then you say that JLS does not mention the implementation. This is good enough!Fennel
@Fennel It's not at all good enough. The correctness of your code should not be dependent on its compilation or execution environment, particularly in the Java language.Rabelais
Yeah but it is dependent on the JDK version. And that is the whole point. You need to verify the implementation for any JDK you are interested in. You mentioned specifically for Oracle JDK (and did not mentioned a specific JDK version). You mentioned that you do not know if the document you read is an authoritative source. Is there a specific Oracle JDK version you are interested in knowing about or a different JDK, like Open JDK?Fowliang
@JoseMartinez When did I mention the Oracle JDK?Rabelais
@JoseMartinez Even within the Oracle JDK, relying on an unspecified feature can break the backward-compatibility guarantees of a later version.Rabelais

© 2022 - 2024 — McMap. All rights reserved.