Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free.

Recently I'm reading the source code of Spring Framework. Something I can't understand goes here:

public Member getMember() {
    // NOTE: no ternary expression to retain JDK <8 compatibility even when using
    // the JDK 8 compiler (potentially selecting java.lang.reflect.Executable
    // as common type, with that new base class not available on older JDKs)
    if (this.method != null) {
        return this.method;
    }
    else {
        return this.constructor;
    }
}

This method is a member of class org.springframework.core.MethodParameter. The code is easy to understand while the comments are hard.

NOTE: no ternary expression to retain JDK <8 compatibility even when using the JDK 8 compiler (potentially selecting java.lang.reflect.Executable as common type, with that new base class not available on older JDKs)

What's the difference between using ternary expression and using if...else... construct in this context?

share|improve this question

4 Answers 4

up vote 42 down vote accepted

When you think about the type of the operands, the problem becomes more apparent:

this.method != null ? this.method : this.constructor

has as type the most specialized common type of both the operands, i.e. the most specialized type common to both this.method and this.constructor.

In Java 7 this is java.lang.reflect.Member, however the Java 8 class library introduces a new type java.lang.reflect.Executable which is more specialized than the generic Member. Hence with a Java 8 class library the result type of the ternary expression is Executable rather than Member.

Some (pre-release) versions of the Java 8 compiler seem to have produced an explicit reference to Executable inside generated code when compiling the ternary operator. This would trigger a class load, and thus in turn a ClassNotFoundException at runtime when running with a class library < JDK 8, because Executable only exists for JDK ≥ 8.

As noted by Tagir Valeev in this answer, this is actually a bug in pre-release versions of JDK 8 and has since been fixed, so both the if-else workaround and the explanatory comment are now obsolete.

share|improve this answer
    
Then how can code compiled with JDK 1.8 run on JDK 1.7. I've known that code compiled with lower version JDK can run on higher version JDK without trouble.Vice versa? –  jddxf 22 hours ago
    
@jddxf Everything is fine as long as you specified the proper class file version and don't use any functionality that isn't available in later versions. Problem are bound to occur, however if such use happens implicitly like in this case. –  dhke 21 hours ago
5  
@jddxf, use -source/-target javac options –  Tagir Valeev 21 hours ago
    
Thank you all,especially dhke and Tagir Valeev, who have given a thorough explanation –  jddxf 7 hours ago

This was introduced in quite old commit at May 3rd, 2013, almost a year before official JDK-8 release. The compiler was under heavy development those times, so such compatibility problems could occur. I guess, the Spring team just tested the JDK-8 build and tried to fix problems, even though they are actually compiler problems. By JDK-8 official release this became irrelevant. Now the ternary operator in this code works fine as expected (no reference to Executable class in compiled .class-file is present).

Currently similar things appear in JDK-9: some code which can be nicely compiled in JDK-8 is failed with JDK-9 javac. I guess, most of such problems will be fixed till the release.

share|improve this answer
    
+1. So, was this a bug in the early compiler? Was that behavior, where it referred to Executable, in violation of some aspect of the spec? Or is it just that Oracle realized that they could change this behavior in a way that would still conform to the spec and without breaking backward-compatibility? –  ruakh 41 mins ago

The chief difference is that an if else block is a statement whereas the ternary (more often known as the conditional operator in Java) is an expression.

A statement can do things like return to the caller on some of the control paths. An expression can be used in an assignment:

int n = condition ? 3 : 2;

So the two expressions in the ternary after the condition need to be coercable to the same type. This can cause some odd effects in Java particularly with auto-boxing and automatic reference casting - this is what the comment in your posted code is referring to. The coercion of the expressions in your case would be to a java.lang.reflect.Executable type (as that's the most specialised type) and that does not exist in older versions of Java.

Stylistically you should use an if else block if the code is statement-like, and a ternary if it's expression-like.

Of course, you can make an if else block behave like an expression if you use a lambda function.

share|improve this answer

The return value type in a ternary expression is affected by parent classes, which changed as described in Java 8.

Hard to see why a cast couldn't have been written.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.