Why doesn't autoboxing overrule varargs when using method overloading in Java 7?
Asked Answered
U

3

19

We have a class LogManager in our Java project which looks like this:

public class LogManager {

    public void log(Level logLevel, Object... args) {
        // do something
    }

    public void log(Level logLevel, int value, Object... args) {
        // do something else
    }
}

When compiling the project with OpenJDK 6 under Debian everyting works fine. When using OpenJDK 7 the build (done with ant) produces the following errors and the build fails:

[javac] /…/LogManager.java:123: error: reference to log is ambiguous,
                      both method log(Level,Object...) in LogManager
                      and method log(Level,int,Object...) in LogManager match
[javac]       log(logLevel, 1, logMessage);
[javac]       ^
[javac] /…/SomeOtherClass.java:123: error: reference to log is ambiguous,
                      both method log(Level,Object...) in LogManager
                      and method log(Level,int,Object...) in LogManager match
[javac]       logger.log(logLevel, 1, logMessage);
[javac]             ^

As long as the 1 is not autoboxed, the method call should be unambiguous as 1 is an int and cannot be upcast to Object. So why doesn't autoboxing overrule varargs here?

Eclipse (installed using the tar.gz from eclipse.org) compiles it no matter if OpenJDK 6 is installed or not.

Thank's a lot for your help!

Edit:

The compiler gets the option source="1.6" and target="1.6" in both cases. The Eclipse compiling note is just meant as a comment.

Unsupportable answered 7/10, 2011 at 15:25 Comment(0)
P
17

I guess it's related to bug #6886431, which seems to be fixed in OpenJDK 7 as well.

The problem is that JLS 15.12.2.5 Choosing the Most Specific Method says that one method is more specific than another one when types of formal parameters of the former are subtypes of formal parameters of the latter.

Since int is not a subtype of Object, neither of your methods is the most specific, thus your invocation is ambiguous.

However, the following workaround is possible, because Integer is a subtype of Object:

public void log(Level logLevel, Object... args) { ... }
public void log(Level logLevel, Integer value, Object... args) { ... } 
Preparative answered 7/10, 2011 at 15:57 Comment(2)
I tried using Integer instead of int and it worked (with both OpenJDK 6 and 7). But with int and a call log(logLevel, 1, logMessage1) my method signature should be more specific as only upcasts and no autoboxing is needed. So I can see that it works this way, but I don't really understand why. I would expect the error with your signatures instead of mine.Unsupportable
+1 as I didn't bother to reread other answers before I came to the same conclusion in a comment.Lytta
L
2

Eclipse uses it's own compiler, so what Eclipse does eventually follows what the SUN / Oracle provided compilers does; however, sometimes (like in this case) there are differences.

This could "go either way" and probably in Java 6, the issue wasn't addressed in detail. Since Java has a strong requirement to reduce the number of "ambiguous" meanings in its environment (to enforce same behavior across many platforms), I'd imagine that they tightened up (or directly specified) the decided behavior in the 7 release.

You just got caught on the "wrong" side of new specification clarification. Sorry, but I think you'll be writing a bit of this

public void log(Level logLevel, Object... args) {
    if (args != null && args[0] instanceof Integer) {
      // do something else
    } else {
      // do something
    }
}

into your new solution.

Lytta answered 7/10, 2011 at 15:37 Comment(5)
Using your solution would work to compile the project, but destroy the beauty of method overwriting :(Unsupportable
@Michael: You could also try if declaring a method log(Level level, int i) that calls log(level, i, new Object[]{}) helps the compiler - I don't have a Java 7 environment at hand right now to try. (Also, method overloading isn't beautiful, precisely because it makes method resolution needlessly complicated causing these kinds of edge cases; at least compared to named and optional parameters which would cover your use case well.)Carmeliacarmelina
This would just lead to an unused method as we never need the method log without any further arguments, i.e., the log messages. Clearly I meant method overloading instead of overwriting. I didn't like it 6 years ago, but now I can see its beauty. And I don't think that it's needlessly complicated as it can be defined very clearly. From my point of view this is a bug of the OpenJDK 7 compiler;)Unsupportable
@Michael, not a bug in the compiler; but, perhaps a bug in the language. When promoting built-in types to Object types, there's a conceptual conflict. Have you tried log(Level, Integer, Object ...)? That might use autoboxing to your favor, without quickly conflicting with the Integer is a Object which leads to the binding with log(Level, Object ...). Personally, I understand the built-in type compromise, but I prefer languages where even constants are Objects. Why not int x = 4; System.out.println(x.toString())?Lytta
int x is not a constant. There are no real constants in Java (just final types). Having ints as non objects has its benefits, as they are more efficient. Allowing something like x.toString() would be restricted to very few functions and the benefit for the programmer wouldn't be so big. I by the way prefer languages from the functional programming world:)Unsupportable
S
0

This is skating way closer to the edge than is prudent. Unless you can find clear language in the spec as to behavior, I'd avoid anything ambiguous like this.

Even if it is in the spec, readers of your code won't have done the language lawyering to know, so you'll need to comment it to explain, and they may or may not read the comment. They may not even consider one of the alternatives - just see one overload that fits, and run with that. An accident waiting to happen.

Scatter answered 7/10, 2011 at 16:11 Comment(1)
No one yet had problems understanding this part of the code. Maybe because it is a project of a computer science chair used only by master and Ph.D. students:) But after all, I thought, that Java considers autoboxing + upcasting more expensive than only upcasting.Unsupportable

© 2022 - 2024 — McMap. All rights reserved.