Home > Software design >  Why won't this code with Java generics compile?
Why won't this code with Java generics compile?

Time:10-28

Consider the following code:

import java.math.BigDecimal;

class Scratch {

  public static class Test<N extends Number> {

    public void foo(Class<N> numberClass) {
      System.out.println(numberClass);
    }

    public void bar() {
      foo(BigDecimal.class);
    }
  }

  public static void main(String[] args)  {
    Test<Number> t = new Test<>();
    t.bar();
  }
}

This fails to compile on line 12 (the call to foo()) with incompatible types: java.lang.Class<java.math.BigDecimal> cannot be converted to java.lang.Class<N>. I don't get it, because the generic N extends Number, and so I should be able to pass BigDecimal.class to a method which takes a Class<N> parameter. TIA for any thoughts!

CodePudding user response:

Suppose a caller did:

var t = new Test<Integer>();
t.bar();

then, bar() would pass a BigDecimal to foo(), which expects an Integer.

CodePudding user response:

The short answer really is that BigDecimal is not necessarily N. It is a little confusing because BigDecimal is within the <N extends Number> bounds, but the current instance's generic type argument can be any type that extends Number, but the bar method assumes BigDecimal.

Check this slightly modified version of the method:

public void bar() {
    new Test<BigDecimal>().foo(BigDecimal.class);
}

That method compiles. Why? Becuase Test<BigDecimal>() has BigDecimal as type argument.

When you call foo(BigDecimal.class), you're assuming that this was instantiated with BigDecimal as type argument, which is not always true, and the the compiler is preventing a bug.


The error in your code would be similar to a hypothetical bar() method in ArrayList<T> that does add("string"), which would be wrong for the same reason (the actual array list instance may be created to hold integers, not strings, so the inside code shouldn't make assumptions)

  • Related