Why invokeSpecial is needed when invokeVirtual exists
Asked Answered
M

3

63

There are three opcodes to invoke Java methods. It is clear that invokeStatic is just for static method invocation.

As far as I know invokespecial is used when invoking constructor and private methods. So, do we need to differenticate private and public method invocation at run time? It could be invoked with same opcode say invokevirtual?

Does JVM deals with private and public method definition? As far as I know public and private keywords is just needed at development phase for encapsulation?

Murat answered 7/12, 2012 at 13:39 Comment(2)
As per you comment, there could be scenario in which invokespecial is required while invoking private method.Murat
possible duplicate of Method invocation of invokespecial and invokeinterfaceScrawl
M
36

http://www.artima.com/underthehood/invocationP.html The link above gives valuable examples clearly which addresing my question.

class Superclass {

    private void interestingMethod() {
        System.out.println("Superclass's interesting method.");
    }

    void exampleMethod() {
        interestingMethod();
    }
}

class Subclass extends Superclass {

    void interestingMethod() {
        System.out.println("Subclass's interesting method.");
    }

    public static void main(String args[]) {
        Subclass me = new Subclass();
        me.exampleMethod();
    }
}

When you invoke main() in Subclass as defined above, it must print "Superclass's interesting method." If invokevirtual were used, it would print "Subclass's interesting method." Why? Because the virtual machine would choose the interestingMethod() to call based on the actual class of the object, which is Subclass. So it will use Subclass's interestingMethod(). On the other hand, with invokespecial the virtual machine will select the method based on the type of the reference, so Superclass's version of interestingMethod() will be invoked.

Murat answered 7/12, 2012 at 14:12 Comment(5)
I can think of no good reason to have code like this. This is hiding, and any good IDE will give you a warning or error if you do this. The subtle difference between the two interestingMethod signatures is enough to confuse the reader as to what's happening.Rhombohedron
@Rhombohedron For example, this might happen when Superclass is a library class that you extend. Maybe you don't even have its source code. You certainly don't care about its private methods. It's quite possible that you add a method to Subclass that happens to have the same name as a private method in Superclass.Hegel
For other readers: Also keep in mind that the Java language has rules and restrictions that the JVM does not. The above is a very good example of "what kind of source code might produce the kind of bytecode being discussed," but that doesn't mean it's good software engineering. :-) For that matter, such source code wouldn't have to be in Java, merely some language that can be compiled to JVM bytecode.Powdery
I thought the same that 'Had it been invokevirtual when Superclass calls interestingMethod() from exampleMethod(). But actually did an experiment by modifying bytecode in Superclass.class file by replacing invokespecial with invokevirtual, the output from running Java Subclass is still "Superclass's interesting method." Also, found that JDK 11 actually compiles Superclass with bytecode instruction invokevirtual directly! While JDK 8 still compiles with invokespecialTroxell
So I think it does NOT matter in this particular case wither it is invokespecial or invokevirtual, because the method being called is a private function, which JVM will check and make a decision to not dispatch based on class.Troxell
B
51

From this site

The answer can be easily found if one reads the Java VM Spec carefully:

The difference between the invokespecial and the invokevirtual instructions is that invokevirtual invokes a method based on the class of the object. The invokespecial instruction is used to invoke instance initialization methods as well as private methods and methods of a superclass of the current class.

In other words, invokespecial is used to call methods without concern for dynamic binding, in order to invoke the particular class’ version of a method.

There are several cases where its important to dispatch a method call "without concern for dynamic binding":

First, when chaining from one constructor to a super-class constructor.

Second, as in super.foo() when calling from a method to a super-class's implementation. If the class, or any sub-class of it overrode that method, then invokevirtual would go to the wrong place.

Third, when a class wants to pick which default version of a method to use as in

interface I { void f(); }
interface J extends I { default void f() { ... } }
interface K extends I { default void f() { ... } }
class C implements J, K {
  @Override public void f() { K.super.f(); }
}

class C above has a diamond inheritance problem, so it needs to pick which default method to invoke. invokespecial allows it to dispatch to K's version, but if K.super.f dispatched via invokevirtual the call would end up back at C.f.

Backwoodsman answered 7/12, 2012 at 13:46 Comment(3)
Unless of course you're doing a super call and the target class has the ACC_SUPER flag (which every modern class will).Lapillus
@Lapillus …and which Java 8 and newer will assume even when it’s not present.Overweight
IIRC they also back ported that to Java 7u51, not that it matters nowadays.Lapillus
M
36

http://www.artima.com/underthehood/invocationP.html The link above gives valuable examples clearly which addresing my question.

class Superclass {

    private void interestingMethod() {
        System.out.println("Superclass's interesting method.");
    }

    void exampleMethod() {
        interestingMethod();
    }
}

class Subclass extends Superclass {

    void interestingMethod() {
        System.out.println("Subclass's interesting method.");
    }

    public static void main(String args[]) {
        Subclass me = new Subclass();
        me.exampleMethod();
    }
}

When you invoke main() in Subclass as defined above, it must print "Superclass's interesting method." If invokevirtual were used, it would print "Subclass's interesting method." Why? Because the virtual machine would choose the interestingMethod() to call based on the actual class of the object, which is Subclass. So it will use Subclass's interestingMethod(). On the other hand, with invokespecial the virtual machine will select the method based on the type of the reference, so Superclass's version of interestingMethod() will be invoked.

Murat answered 7/12, 2012 at 14:12 Comment(5)
I can think of no good reason to have code like this. This is hiding, and any good IDE will give you a warning or error if you do this. The subtle difference between the two interestingMethod signatures is enough to confuse the reader as to what's happening.Rhombohedron
@Rhombohedron For example, this might happen when Superclass is a library class that you extend. Maybe you don't even have its source code. You certainly don't care about its private methods. It's quite possible that you add a method to Subclass that happens to have the same name as a private method in Superclass.Hegel
For other readers: Also keep in mind that the Java language has rules and restrictions that the JVM does not. The above is a very good example of "what kind of source code might produce the kind of bytecode being discussed," but that doesn't mean it's good software engineering. :-) For that matter, such source code wouldn't have to be in Java, merely some language that can be compiled to JVM bytecode.Powdery
I thought the same that 'Had it been invokevirtual when Superclass calls interestingMethod() from exampleMethod(). But actually did an experiment by modifying bytecode in Superclass.class file by replacing invokespecial with invokevirtual, the output from running Java Subclass is still "Superclass's interesting method." Also, found that JDK 11 actually compiles Superclass with bytecode instruction invokevirtual directly! While JDK 8 still compiles with invokespecialTroxell
So I think it does NOT matter in this particular case wither it is invokespecial or invokevirtual, because the method being called is a private function, which JVM will check and make a decision to not dispatch based on class.Troxell
R
1

Thanks for reading out that explanation: Please don't forget to upvote if it helps you out to identify assembly instruction creation during method call Here I am explaining static vs dynamic binding.

First of all I am going to tell you that invokeStatic, invokeSpecial, invokeVirtual, invokeInterface etc are the assembly instructions which are generate by the compiler after compilation process. As we all knows that we got a .class file format after compilation and we can't read it out. But java provide a tool for that named "javap".

We can read out our .class file assembly instructions by using javap command. By default we can't see private method assembly instructions so we need to use -private with it. Below are the commands to see java compiler generated assembly instructions:

  1. Imaging you have A.java class

    class A { public void printValue() { System.out.println("Inside A"); }

    public static void callMethod(A a) { a.printValue(); } }

  2. open cmd prompt and go to the folder which contains that java file A.java.

  3. run javac A.java.

  4. Now A.class file is generated which contains assembly instructions but you can't read it.

  5. Now run javap -c A

  6. You can see assembly generation for your method call --> a.printValue();

  7. If printValue( ) method is private you need to use javap -c -private A .

  8. You can make your printValue( ) private / static / public / private static both.

  9. One more thing to keep in mind that first compiler check the object on which the method is getting called. Then find its Class Type and found that method in that class if available or not.

Note : Now keep in mind that if our calling method is static then invokeStatic assembly is generated, if its private then invokeSpecial assembly instruction is generated and if its public then invokeVirtual instruction is generated. public method never mean that every time invokeVirtual instruction is generated. In case of super.printValue() call from the subclass of A is exceptional case. i.e. if A is parent class for B and B contains the same method printValue() then it will generate invokeVirtual(dynamic) but if printValue() in B have super.printValue() as its first statement then invokeStatic is generated even if printValue() of A is public.

Let's try this too:

class B extends A
{
public void printValue()
{
super.printValue();// invokeStatic
System.out.println("Inside B");
}

}

public class Test
{
public static void main(String[] arr)
{
    A a = new A();
    B b = new B();
    A.callMethod(a);// invokeVirtual
    A.callMethod(b);// invokeVirtual
}
}

--> save it by Test.java --> run javac Test.java --> javap -c -private Test

Rampant answered 9/10, 2018 at 5:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.