Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does this generic code produce a NoSuchMethodError?

Say you have a method that

  • takes a threshold and an input
  • raises an exception if the input is less than the threshold
  • otherwise returns the input

it would look something like this:

<N extends Number & Comparable<N>, S extends N> S ensureLessThan(N threshold, S input) {
    if (input.compareTo(threshold) >= 0) {
        throw new IllegalArgumentException("Input " + input + " is not less than " + threshold);
    }
    return input;
}

When run, this method throws a NoSuchMethodError:

Exception in thread "main" java.lang.NoSuchMethodError: java.lang.Number.compareTo(Ljava/lang/Object;)I

Adding what looks like a redundant cast makes it work:

...
    if (((N) input).compareTo(threshold) >= 0) {
...

So what's going on here?

UPDATE: My Java version is

java version "1.6.0_37"
Java(TM) SE Runtime Environment (build 1.6.0_37-b06-434-11M3909)
Java HotSpot(TM) 64-Bit Server VM (build 20.12-b01-434, mixed mode)

And here's a runnable example: https://gist.github.com/4526536

like image 594
Mike Craig Avatar asked Jan 13 '13 21:01

Mike Craig


People also ask

What causes Java Lang NoSuchMethodError?

lang. NoSuchMethodError is a runtime error in Java which occurs when a method is called that exists at compile-time, but does not exist at runtime. The Java Garbage Collector (GC) cannot free up the space required for a new object, which causes a java. lang.

What Why are generic used?

In a nutshell, generics enable types (classes and interfaces) to be parameters when defining classes, interfaces and methods. Much like the more familiar formal parameters used in method declarations, type parameters provide a way for you to re-use the same code with different inputs.

How do you handle no such method exception?

Dig into the do. something code, decompiling it if you don't have the source, and see what is actually thrown by it. Possibly if the exception gets thrown it might get wrapped in another exception. Make sure that anything thrown by the method does not get eaten (including by your own logging of it).


3 Answers

I suspect it's a bug in the compiler. It has staticly bound to Number.compareTo(Object) which doesn't exist, when it should be Comparable.compareTo(Object).

like image 142
Peter Lawrey Avatar answered Oct 21 '22 21:10

Peter Lawrey


if (threshold.compareTo(input) < 0) {

will work.

It certainly appears to be a compiler error. The reason this works is that the compiler generates a checked cast to Comparable. Whereas the other way around it does not.

ensureLessThan(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;
  L0
        LINENUMBER 11 L0
        ALOAD 1
        CHECKCAST java/lang/Comparable
        ALOAD 2
        INVOKEINTERFACE java/lang/Comparable.compareTo (Ljava/lang/Object;)I
        IFGE L1

versus

ensureLessThan(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;
        LINENUMBER 11 L0
        ALOAD 1
        ALOAD 2
        INVOKEVIRTUAL java/lang/Number.compareTo (Ljava/lang/Object;)I
        IFGE L1
like image 39
BevynQ Avatar answered Oct 21 '22 21:10

BevynQ


That's strange, the following code compiles and runs from my side:

/**
 * @author Buhake Sindi
 * @since 14 January 2013
 *
 */
public class Test {

    public static <N extends Number & Comparable<N>, S extends N> S ensureLessThan(N threshold, S input) {
        if (input.compareTo(threshold) >= 0) {
            throw new IllegalArgumentException("Input " + input + " is not less than " + threshold);
        }
        return input;
    }

    public static void main(String[] args) {
        ensureLessThan(10, 5);
    }
}

Tested in Eclipse Juno (Java EE), with the following JDK:

  1. JDK 1.6.0_21.
  2. JDK 1.7.0_09-b05
like image 22
Buhake Sindi Avatar answered Oct 21 '22 21:10

Buhake Sindi