views:

81

answers:

3

Hi, I got this from a berkley cs data structures webcast:

class A {
  void f() {System.out.println("A.f");}
  void g() {f();}
 // static void g(A y) {y.f();}
}

class B extends A {
  void f(){
    System.out.println("B.f");
  }
}

class C {
  static void main (String[] args){
    B aB = new B();
    h (aB);
  }

  static void h (A x) {x.g();}
  //static void h (A x) {A.g(x);}  what if this were h 

}

Can you tell me what prints out and why? The instructor said B.f but I do not understand why. I thought it was A.f. Thank you (no, I am not in the class, just trying to learn.)

edit: sorry for the mistakes I was copying it from a video lecture.

+1  A: 

The reason "B.f" prints is because the implementation of a method is determined by an object's run-time type, not its compile-time type. It's like the virtual keyword in C++.

Once you construct a B, you know that calling its f method will print "B.f", even if the B is being referred to as an A.

Also, your A.f method is missing a close brace.

danben
so I believe it is the (A x) that is fooling me. The method of class c static void h (A x) can use A as the parameter type because B is an A. but it really has nothing to do with changing the fact that I can still passing a constructed B. Is that right?
johnny
Yes, that's right. A method that expects an `A` can receive an instance of any subtype `S` of `A`. If `S` overrides a method that `A` defines, then that is the implementation that will be used.
danben
+1  A: 

that code is not correct, A.g() does not take any arguments.

static void h (A x) {A.g(x);}

A.g(x); is trying to call a static method on A called g with x as an argument. That is not possible with the code example you posted.

other than the wrong code, the reason is B overrides the method f() with its own implementation. Thus any instances of B such as B x = new B(); will call the f() that B defines not the A.f(). The only way to reach the A.f() would be from inside an instance of B by calling super.f();

fuzzy lollipop
it wasn't at first...my mistake it has been fixed.
johnny
it wasn't when I wrote the answer
fuzzy lollipop
Just to clarify: There are two errors in this line of code. The first is that A.g does not take any arguments. The second is that g is not a static function, and so must be called with an object, not a class name. The compiler will kick this out.
Jay
i added the commented out static for the alternate
johnny
+2  A: 

This example demonstrates the power of object-oriented programming.

Because ab is an instance of B, any method call on ab will use the functions defined in B, even if these functions are called indirectly via a function defined in a superclass.

The example is so abstract that the virtue of this may not be clear. Let me make a slightly more realistic example:

class Employee
{
  ... bunch of stuff ...
  void calcPay()
  {
    pay=hoursWorked*hourlyRate;
  }
  void produceCheck))
  {
    calcPay();
    calcTaxes();
    calcBenefitDeductions();
    printCheck();
  }
}
class Salesman extends Employee
{
  void calcPay()
  {
    pay=sales*commissionRate;
  }
}
... somewhere else ...
for (Employee employee1 : employeeList)
{
  employee1.produceCheck();
}

I'm leaving out all sorts of detail to make the point: This code won't compile.

But here's the point: Salesman have a different method of calculating their pay then other employees: They're paid on commission instead of hourly. (In real life, we'd presumably also have salaried employees, but as I say, I'm simplifying.) The function to calculate pay of either type of employee is called from within a bigger function that also does other things. The beauty of object-oriented programming is that we can call the outside function and not care whether the object we are calling it against is a regular Employee or a Salesman. Each object knows what it is and calls the right function. In the last few lines of the example, we have some structure that includes both regular Employees and Salesmen, and we can loop through and process them all without having to check their type. Without OOP, we'd have to constantly write code like: "if (type == SALESMAN) ... else if (type == HOURLY) ..."

Jay
+1. Good, but know that it is a better practice not to instantiate non-leaf classes, just as a tip for beginners. So a better design here might be to make Employee and calcPay() abstract, and subclass both (eg) HourlyEmployee and Salesman. Yes, as presented, it would work, but you avoid problems if you don't instantiate non-leaf classes.
Carl Manaster
I agree. Indeed I started out constructing the example with an abstract class and two subclasses but then decided that was adding complexity unnecessary to make the point. While I don't make it an absolute rule, it is certainly a good rule of thumb, and in this case in real life it certainly would be better to make Employee abstract as you say.
Jay