This example code may clarify some of the relationship between fields and methods. Consider these two classes in Java: class Greeting { String intro = "Hello"; String target() { return "world"; } } class FrenchGreeting extends Greeting { String intro = "Bonjour"; String target() { return "le monde"; } } What do you think happens if you run the following program? public static void main(String argv[]) { Greeting english = new Greeting(); Greeting french = new FrenchGreeting(); System.out.println(english.intro + ", " + english.target()); System.out.println(french.intro + ", " + french.target()); System.out.println(((FrenchGreeting) french).intro + ", " + ((FrenchGreeting) french).target()); } The answer is Hello, world Hello, le monde Bonjour, le monde This result is somewhat surprising, especially the multilingual second line. The first line of output makes perfect sense. Both parts are in English, which is what you'd expect, since you're using the english object. The second line is a surprise, since it combines an English introduction with a French target, even though the object in both cases is the same object. Now the third line seems pretty surprising as well. The reference is the same one used on the second line, but now you get a different word from the same field of the same object. The only thing changed there is a cast, which does not affect the type of the underlying object. This example demonstrates the difference between the virtual dispatch mechanism and the way fields are accessed. Remember that a class inherits all non-static fields from its parent. In the case of FrenchGreeting, this means that the object has two different fields named intro: one in English and one in French. Figure 4.7 shows what the memory space looks like during the invocation of main. Variable 1 is english, and variable 2 is french. The Oolong equivalent of english.intro is aload_1 ; Get english getfield Greeting/intro Ljava/lang/String; ; Get the intro field Figure 4.7. Memory layout of Greeting and FrenchGreeting objectsThe translation of french.intro is aload_2 ; Get french getfield Greeting/intro Ljava/lang/String; ; Get the intro field As we said in section 4.4, the target field is chosen based on both the class name and the field name. The Java compiler uses Greeting here because that's the declared type of the french variable. If you force the Java compiler to think of the french variable as a FrenchGreeting with ((FrenchGreeting) french).intro then the Java compiler generates this code instead: aload_2 ; Get french checkcast FrenchGreeting ; Check type getfield French/intro Ljava/lang/String; ; Get the intro field This code explains why you got different results for the first part (the intro) of lines 2 and 3. However, it doesn't explain why you got the same results for the second part (the target). The reason is the virtual dispatch mechanism. Here is a translation of the three calls to target(): ; english.target() aload_1 ; Get english invokevirtual Greeting/target ()Ljava/lang/String; ; Call target ; french.target() aload_2 ; Get french invokevirtual Greeting/target ()Ljava/lang/String; ; Call target ; ((FrenchGreeting) french).target() aload_2 ; Get french checkcast FrenchGreeting ; Check type invokevirtual FrenchGreeting/target ()Ljava/lang/String; ; Call target The generated code is a little different in each case. In the first case, it loads the english object from variable 1 and calls target, using the Greeting class. In the second case, it uses the same method name and class, but the method implementation that is actually called is different. That's because the invokevirtual instruction looks at the actual type of the object, not at the type given in the arguments, to determine which method to invoke. See section 4.5.1 for the details of virtual invocation. Referring to Figure 4.7, you can see that the object in variable 2 contains a reference to an object of class FrenchGreeting. Looking up the target method in this class yields the FrenchGreeting implementation of target, which returns the string le monde instead of world. In the third case, the object is the same as the second case, which means that the class of the object is also the same. Therefore, when you invoke the target method, you get le monde again. |