About Java method binding

Today I'm going to write an article about Java method bindings. I usually study Java and wasn't very conscious of it, so I'll organize it properly at this time.

Prior knowledge

Let's review the cast as prior knowledge. Casts have explicit and implicit conversions. Please see the following URL for details. https://techacademy.jp/magazine/28486 You should also know about compile-time and run-time timing. Compile time is the timing when the Java file you wrote is converted to bytecode. Execution is the time when the JVM actually executes the bytecode. If you are developing in Eclipse, usually when you write the code, it will be compiled without permission, and the code will be executed when you press the execute button. When developing in a terminal etc., compile with the javac command to create a class file, and then actually execute it with the java command. The terms "tie" and "bind" used in the article all have the same meaning. (I'm sorry it's confusing ...)

What is method binding in the first place?

I didn't even know the meaning of this word, but in simple terms, it's like a mechanism in Java that connects (binds) a method call, the signature of the called method, and the implementation part. The signature is the method name + argument, and the implementation part is the processing inside {}. The method is a lump of these two, but for now, think about it separately. If you don't understand this, you may stumble in unexpected places (although I'm not an engineer yet, so I can't say it's great ...). Let's look at it concretely.

Animal.java


public class Animal {
      public void eat(double quantity) {
             System.out.println("Animals" + quantity + "g ate.");
      }
}

Human.java


public class Human extends Animal {
      public void eat(int quantity) {
            System.out.println("People are" + quantity + "g ate.");
      }
}

Test.java


public class Test {
     public static void main(String[] args) {
           Animal animal = new Human();
           int quantity = 500;
           animal.eat(quantity);  // ?
     }
}

Suppose all the classes are in the same package. What will be output if I run Test.java here? And why?

Method binding

The correct answer is "The animal ate 500.0g." Why. At first, I thought that "Human ate 500g" should be output because Human is assigned as the reference destination. The int type is also passed as an argument in the Test class. But in reality the answer was different. Let's take a look at when method bindings occur in Java to understand what happened. See the table below.

Method type Binding at compile time Binding at run time
non-static method Method signature Method implementation
static method Method signature, method implementation

Since eat is a non-static method this time, I will explain based on this (the idea is the same for static methods).

Compile binding

At compile time, Java associates the called method with its signature. In other words, it may be said that the compiler determines the signature of the method called each time. Let's think about this rule along with the code we are looking at right now. The last line in Test.java looks like this:

Test.java


animal.eat(quantity);

First, the compiler looks at the declaration type (type) of the variable animal. The type of animal is Animal type. The compiler then begins the search, "Yeah, yeah. This variable is the type of the Animal class. Let's find out if there is a method in this class that is currently being called." At this time, the search range also includes methods that are compatible with the method being called. Actually in the Animal class

Animal.java


eat(double quantity)

The signature is defined. In the caller, the int type is passed as an argument, but the conversion from int to double is done arbitrarily (without explicitly casting), so the compiler says, "This is the method that is being called now. It is a method compatible with ", and it connects the method call of animal.eat (quantity) with the eat method (signature) that takes a double type as an argument. At this point the compiler has determined that the eat argument is of type double. And it can no longer be changed at runtime. Let's actually check it.

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class Human
       3: dup
       4: invokespecial #3                  // Method Human."<init>":()V
       7: astore_1
       8: sipush        500
      11: istore_2
      12: aload_1
      13: iload_2
      14: i2d
      15: invokevirtual #4                  // Method Animal.eat:(D)V
      18: return
}

It compiles each file with the javac command from the terminal and then looks at the contents of the compiled code with javap -c Test. Here are the lines that you should be aware of.

15: invokevirtual #4                  // Method Animal.eat:(D)V

This corresponds to the method call part of the Test class, animal.eat (quantity). (D) indicates that the argument is of double type and V indicates that the return value is void. invokevirtual means that the actual implementation is determined at runtime. The code is executed at Runtime according to the instructions of this bytecode. In other words, the compiler only associates "the method being called is the Animal eat method" as described above, and the content of the process is determined by the JVM at runtime.

Runtime binding

All you have to do is execute the animal.eat (quantity) method according to the bytecode instructions. Earlier I said that the compiler would go to see the declaration type of the variable animal. The JVM starts the search on the assigned object. In other words

Test.java


Animal animal = new Human();

The compiler looks at the left side (Animal) of this expression for a series of bindings, while the JVM first looks at the object on the right side (Human). Then, since this is an object of the Human class, it tries to find the method of eat: (D) V in the Human class. On the other hand, since the Human class contains eat: (I) V (I is an int type), I can't find a matching method. Therefore, the JVM searches for the corresponding method from the inheritance relationship. In other words, if you look at the parent class that the class inherits and if it is not there, you go to see the parent class, and so on, going up the hierarchy and looking for eat: (D) V. In this case, there was a corresponding method in the Animal class that went up one level, so the implementation part in this is bound to the calling method (animal.eat (quantity)) and processing is executed. As a result, the output was "The animal ate 500.0g."

Method binding example

Finally, let's look at an example of method binding.

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list); // [1, 2, 3, 4]

list.remove(3);
System.out.println(list); // [1, 2, 3]

The List type remove method is overloaded with two methods that take different types of arguments, List.remove (int index) and List.remove (Object o) (https://docs.oracle.com/javase). /jp/8/docs/api/java/util/List.html). Here, remove has an int type argument, so list.remove (3) and List.remove (int index) are bound at compile time. And at runtime, the process (implementation part) in ArrayList.remove (int index) is bound to list.remove (3). As a result, the 4 in the third index of this list is removed and displayed as [1, 2, 3]. Nothing has changed so far. But what about the following example?

Collection<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
System.out.println(list); // [1, 2, 3, 4]

list.remove(3);
System.out.println(list); // [1, 2, 4] ← ??

The only thing that has changed is that the list container has changed from a List type to a Collection type. The result is displayed as [1, 2, 4]. The number 3 itself was removed, not the third in the index. Because Collection has only one remove method, Collection.remove (Object o). The compiler binds a remove method that takes this Object type as an argument to list.remove (3). As a result, the argument 3 in list.remove (3) is treated as an Object, not an index. Actually, when I check it with the javap command as before, it is displayed as Collection.remove: (Ljava / lang / Object;) Z (Z is a Boolean type). Then, following the bytecode instructions, the JVM will now execute ArrayList.remove (Object o) instead of ArrayList.remove (int index). As a result, [1, 2, 4] was displayed on the screen. If you didn't know about method bindings, you might have programmed with the assumption that the third index would normally be removed. If this happens, it will cause a failure. I thought that it would be a great advantage to know this mechanism to avoid it, so I wrote this article. Thank you for reading this far.

Recommended Posts

About Java method binding
About method splitting (Java)
Java method
java (method)
[Java Silver] About equals method
Java method
[Java] method
[Java] method
About Java interface
[Java] About Java 12 features
[Java] About arrays
Java8 method reference
Something about java
Where about java
About Java features
About the method
[Java] forEach method
About Java threads
[Java] About interface
About Java class
About Java arrays
About java inheritance
About interface, java interface
java8 method reference
[Java] Random method
[Java] split method
About java var
About Java literals
About Java commands
Output about the method # 2
About Java functional interface
About No Method Error
Java, about 2D arrays
About class division (Java)
JAVA DB connection method
About [Java] [StreamAPI] allMatch ()
Java learning 2 (learning calculation method)
[Java] About Singleton Class
Java learning memo (method)
[Java] About anonymous classes
Studying Java 8 (see method)
[Java Silver] About initialization
About the length method
About Java Array List
About Java Polymorphism super ()
About inheritance (Java Silver)
Java programming (class method)
About Java String class
About Java access modifiers
About Java lambda expressions
About Java entry points
About Java 10 Docker support
Personal summary about Java
About the map method
About the ancestors method
All about Java programming
[Java] Basic method notes
About the to_s method.
About java abstract class
A note about Java GC
What I researched about Java 8