“Strange” things about java byte code and consequences for API evolution


Eike posted lately about interesting problem when changing the return type of a method with respect to callers of the method.

In his sample he switched from a void to a String return type and suddenly old byte code was not able to call the method anymore which sounds strange to most java devs because they learned that a method is unique if it has different parameter types.

This restriction although is something of language Java (other JVM language may allow method overloading by return type) not the JVM and there are good reasons that even javac makes use of!

Think about the following simple snippet:

public class ByteCodeSample {
	public static class Base {
		public Object test() {
			return null;
		}
	}

	public static class Sub extends Base {
		@Override
		public String test() {
			return "";
		}
	}

	public static void main(String[] args) {
		Sub s = new Sub();
		s.test();
	}
}

Here the subclass Sub returns a more specific type than the base one and of you look at the byte code generated you’ll notice that there are 2 methods test() generated:

Thomass-MacBook-Pro-Retina:bin tomschindl$ javap -c ByteCodeSample\$Sub.class 
Compiled from "ByteCodeSample.java"
public class ByteCodeSample$Sub extends ByteCodeSample$Base {
  public ByteCodeSample$Sub();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method ByteCodeSample$Base."<init>":()V
       4: return

  public java.lang.String test();
    Code:
       0: ldc           #16                 // String
       2: areturn

  public java.lang.Object test();
    Code:
       0: aload_0
       1: invokevirtual #19                 // Method test:()Ljava/lang/String;
       4: areturn
}
Thomass-MacBook-Pro-Retina:bin tomschindl$ 

So when we call test() in main() we would be in trouble if the return type would not be in the call signature because we would not know which of the 2 methods we need to call hence the byte code looks like this:

Thomass-MacBook-Pro-Retina:bin tomschindl$ javap -c ByteCodeSample.class 
Compiled from "ByteCodeSample.java"
public class ByteCodeSample {
  public ByteCodeSample();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #16                 // class ByteCodeSample$Sub
       3: dup
       4: invokespecial #18                 // Method ByteCodeSample$Sub."<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #19                 // Method ByteCodeSample$Sub.test:()Ljava/lang/String;
      12: pop
      13: return
}
Thomass-MacBook-Pro-Retina:bin tomschindl$ 

One could have also found out about this JVM thing at the java level by looking at the reflection API-JavaDoc Class.getMethod(String, Class...) which states the following:

To find a matching method in a class or interface C: If C declares exactly one public method with the specified name and exactly the same formal parameter types, that is the method reflected. If more than one such method is found in C, and one of these methods has a return type that is more specific than any of the others, that method is reflected; otherwise one of the methods is chosen arbitrarily.

Note that there may be more than one matching method in a class because while the Java language forbids a class to declare multiple methods with the same signature but different return types, the Java virtual machine does not. This increased flexibility in the virtual machine can be used to implement various language features. For example, covariant returns can be implemented with bridge methods; the bridge method and the method being overridden would have the same signature but different return types.

So remember at the JVM level a method signature is made up from:

  1. Name
  2. Parameter types
  3. Return type

This means changing the return type is a binary incompatible change change which requires a major version update!

Advertisement
This entry was posted in Uncategorized. Bookmark the permalink.

1 Response to “Strange” things about java byte code and consequences for API evolution

  1. madeat says:

    Thank you very much for this explanation!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.