patternjavaMinor
Inheritance, and dynamic access to members/attributes and methods in Java-like languages
Viewed 0 times
languageslikejavainheritanceattributesmethodsmembersdynamicandaccess
Problem
I have a question about inheritance in Java-like OO programming languages. It came up
in my compiler class, when I explained how to compile methods and their invocation. I was using Java as example source language to compile.
Now consider this Java program.
When you run it, you get the following result.
The interesting cases are those that happen with the object
static type
it follows that method invocations are not affected by the compile-time type of the object the method is invoked with. But
A student asked about this difference: should not the access of members and methods be
consistent with each other?
I could not come up with a better answer than "that's the semantics of Java".
Would you know a crisp conceptual reason why members and methods are
different in this sense? Something I could give my students?
Edit: Upon further investigation, this seems to be a Java idiosyncrasy: C++ and C# act differently, see e.g. Saeed Amiri's comment below.
Edit 2: I just tried out the corresponding Scala program:
```
class A {
val x = 0;
def f () : Unit = { System.out.println ( "A:f" ); } }
class B extends A {
override val x = 1;
override def f () : Unit = { System.out.println ( "B:f" ); } }
object Main {
def main ( args : Array [ String ] ) = {
var a : A = new A ();
var b : B = new B ();
var ab : A = new B ();
in my compiler class, when I explained how to compile methods and their invocation. I was using Java as example source language to compile.
Now consider this Java program.
class A {
public int x = 0;
void f () { System.out.println ( "A:f" ); } }
class B extends A {
public int x = 1;
void f () { System.out.println ( "B:f" ); } }
public class Main {
public static void main ( String [] args ) {
A a = new A ();
B b = new B ();
A ab = new B ();
a.f();
b.f();
ab.f();
System.out.println ( a.x );
System.out.println ( b.x );
System.out.println ( ab.x ); } }When you run it, you get the following result.
A:f
B:f
B:f
0
1
0The interesting cases are those that happen with the object
ab ofstatic type
A, which is B dynamically. As ab.f() prints outB:fit follows that method invocations are not affected by the compile-time type of the object the method is invoked with. But
System.out.println ( ab.x ) prints out 0, so member access is affected by compile-time types. A student asked about this difference: should not the access of members and methods be
consistent with each other?
I could not come up with a better answer than "that's the semantics of Java".
Would you know a crisp conceptual reason why members and methods are
different in this sense? Something I could give my students?
Edit: Upon further investigation, this seems to be a Java idiosyncrasy: C++ and C# act differently, see e.g. Saeed Amiri's comment below.
Edit 2: I just tried out the corresponding Scala program:
```
class A {
val x = 0;
def f () : Unit = { System.out.println ( "A:f" ); } }
class B extends A {
override val x = 1;
override def f () : Unit = { System.out.println ( "B:f" ); } }
object Main {
def main ( args : Array [ String ] ) = {
var a : A = new A ();
var b : B = new B ();
var ab : A = new B ();
Solution
I suspect that this is intimately related to shadowing of fields in Java.
When writing a derived class, I can, as in your example, write a field
The variable
When writing a derived class, I can, as in your example, write a field
x that shadows the definition of x in the base class. This means that in the derived class, the original definition is no longer accessible via name x. The reason Java allows this is so that derived class implementations are less dependent on the base class implementation details, thereby (only partially) avoiding the fragile base class problem. For instance, if the base class did not originally have a field x, but then subsequently introduced one, Java's semantics avoid this breaking the derived class implementation.The variable
x in the derived class can have a completely different type than the variable in the base class – I tested this, it works. This is in stark contrast to method overriding, where the type needs to be sufficiently compatible (aka, the same). So when I have a variable of type A in your example, the only type I can derive for it is the one specified in class A (or above). As this can be different from the type declared in class B, it is only type safe to return the value of the field x from class A. (And naturally, the semantics must be the same regardless of what the type of the field is in class B.)Context
StackExchange Computer Science Q#6782, answer score: 6
Revisions (0)
No revisions yet.