Jobs
Jobs

Reputation: 1307

Java byte code and no such method error

We might seen the error:

java.lang.NoSuchMethodError: com.nhn.user.UserAdmin.addUser(Ljava/lang/String;)V.

This says that the method addUser with no return type is not found. This can be due to the reason that, the library might be updated, but the byte code of our application is still old.

But when we study Java overload, we can know that Java does not support overloading with only a change in return type.

Since this is correct, why byte code noting down the return type of the called method, and raising error when the library is updated with a return type for the method.

Upvotes: 3

Views: 852

Answers (2)

Holger
Holger

Reputation: 298213

Java bytecode is strongly typed and verified, which implies that the caller’s code must be compatible to what the invoked method will return. So even if the caller’s method reference did not contain the expected return type, the code did still contain implicit assumptions, e.g. trying to do long arithmetic with the result indicates that the method is expected to return long rather than Object or void.

Having the method references indicating the expected return type simplifies verification and makes the entire process more efficient. You can verify the correctness of a method’s code using expected method signatures without performing an actual linkage. When a method invocation instruction is finally linked, there is no need to verify the executable code, only the signatures have to match.

That’s why Java byte code had been designed that way, even if Java source code could not define different return types, at least in the earlier versions. Starting with Java 5, the rules are not that strict anymore.

Consider the following interface:

interface StringFunction<R> {
    R apply(String input);
}

Due to type erasure, it will have a method Object apply(String input) on the byte code level.

Now consider the following implementation class:

class Length implements StringFunction<Integer> {
    public Integer apply(String input) {
        return input.length();
    }
}

Not only is declaring a more specific return type allowed, it is actually required by the Java language as according to the Generic type system, it inherits an abstract method Integer apply(String) from StringFunction<Integer>.

On the byte code level, it will have the actual implementation method Integer apply(String) as well as a bridge method Object apply(String input) formally fulfilling the contract of the interface on the byte code level and delegating to the actual implementation method.

Since Generics effectively allow a narrowing of the return type, there was no reason to deny it for non-Generic methods, hence, Java allows so called covariant return types since Java 5 as well:

class Base {
    Object getValue() {
        return null;
    }
}
class Sub extends Base {
    @Override String getValue() {
        return "now a string";
    }
}

so it is possible to produce classes having multiple methods with the same parameter types but different return types, though not by overloading.

These cases could be handled alternatively, e.g. by defining that methods are distinguished by parameter types only, and their return type must be the same or more specific, to be compatible with covariant return types, but that would imply that a JVM had to eagerly resolve all return types when building the method table of a class, to verify whether the type really is more specific. And still, it would require the return type to be encoded to establish a proper contract between caller and callee.

Upvotes: 1

AppWriter
AppWriter

Reputation: 245

The method might have been declared in an interface and then overloaded. So when you changed the implemented version of the method, it caused an error. If the return type doesn't match, but the name is the same, an error still results. Changing just the return type can't be done because the compiler wouldn't be able to know which return type you want to use when you call the method.

Upvotes: 0

Related Questions