Inheritance - Why dynamic binding in subtype polymorphism


#1

This question is for Java as well. Following example is from Scala book.
It’s making call to demo() using supertype variable e: Element.

Book says “This means that the actual method implementation invoked is determined at run time based on the class of the object, not the type of the variable or expression.”

Following is example from book. My question is why compiler is not able to infer that at compile time? When caller says invokeDemo(new UniformElement) class of the Object is already defined there - UniformElement. What more it needs from runtime?

abstract class Element {
    def demo() = {
      println("Element's implementation invoked")
    }
}
  class ArrayElement extends Element {
    override def demo() = {
      println("ArrayElement's implementation invoked")
    }
}
  class LineElement extends ArrayElement {
    override def demo() = {
      println("LineElement's implementation invoked")
    }
}
  // UniformElement inherits Element's demo
class UniformElement extends Element

def invokeDemo(e: Element) = {
         e.demo()
}

invokeDemo(new ArrayElement)
invokeDemo(new LineElement)
invokeDemo(new UniformElement)

#2

(Meta-note: this stuff is much easier to read if you surround code blocks with triple-backticks, alone on a line, before and after the code block.)

It’s largely about how invokeDemo() itself is defined. You have it here as:

def invokeDemo(e: Element)

This is telling the compiler, at the definition site, that all it is allowed to know is that e is an Element – it doesn’t know anything else. Even though the call site knows that it is a UniformElement, the definition doesn’t know that.

If you instead defined it as:

def invokeDemo[E <: Element](e: E)

That tells the compiler that e is a subtype of Element. In this case, the compiler can be somewhat smarter about other possibilities. (There are limitations, but this is a powerful and common mechanism.)

Basically, the compiler only knows what you let it know – if you tell it that e is an Element, it will believe you, even if the runtime reality is more complex.

(Note that there are limitations to how clever the compiler can be, because Scala generally doesn’t think in terms of the program as a whole, only in terms of this file and what it imports. So in general, the definition site can’t know much about the call site. And by and large, you don’t want definitions to know too much about their calls: that quickly leads to incomprehensible code.)