Question

I'm trying to write method that converts a two digit number to 2000 + the number, returns all other numbers as they are and returns null when a null is passed as an argument.

This implementation works as intended

private Integer convertTo4Digits(Integer modelYear) {
    boolean isTwoDigit = modelYear != null && modelYear < 100;
    if (isTwoDigit) {
        return 2000 + modelYear;
    } else {
        return modelYear;
    }
}

But this one fails with a NPE in the return statement when called with NULL.

private Integer convertTo4Digits(Integer modelYear) {
    return (modelYear != null && modelYear < 100) ? (2000 + modelYear) : modelYear;
}

Or is this a Bug? I'm using Eclipse Keple with JDK 1.7.0_04

Was it helpful?

Solution

I think the answer can be found in chapter 15.25 of the JLS

If one of the second and third operands is of primitive type T, and the type of the other is the result of applying boxing conversion (§5.1.7) to T, then the type of the conditional expression is T.

So when either the second or third operand is a primitive type the expression's type is a primitive. Thus if you pass a null reference the branch : modelYear will be executed. But since one operand is primitive it must be unboxed. This causes the NPE.

You can also see this if you take a look at the generated byte code

private convertTo4Digits(Ljava/lang/Integer;)Ljava/lang/Integer;
 L0
  LINENUMBER 46 L0
  ALOAD 1
  IFNULL L1
  ALOAD 1
  INVOKEVIRTUAL java/lang/Integer.intValue()I
  BIPUSH 100
  IF_ICMPGE L1
  SIPUSH 2000
  ALOAD 1
  INVOKEVIRTUAL java/lang/Integer.intValue()I
  IADD
  GOTO L2
 L1
  LINENUMBER 47 L1
  ALOAD 1
  INVOKEVIRTUAL java/lang/Integer.intValue()I
 L2
  LINENUMBER 46 L2
  INVOKESTATIC java/lang/Integer.valueOf(I)Ljava/lang/Integer;
  ARETURN 

Your own answer solves the problem because you are casting the second operand to Integer

(modelYear != null && modelYear < 100) ? (Integer) (2000 + modelYear) : modelYear;

and therefore neither the second nor the thrid operand are of primitive type. Thus the rule of the JLS I posted above is not applied and the NPE is gone.

OTHER TIPS

Oh darn, this one works (note the explicit cast to Integer):

 (modelYear != null && modelYear < 100) ? (Integer) (2000 + modelYear) : modelYear;

The problem is: the first branch of the ternary operator determines the result type of the operator: int in the version of the question

Now the modelYear (null) gets unboxed which causes the NPE right before it gets boxed again.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top