Programming Languages and Techniques (CIS120) Lecture 26 March 26, 2015 Inheritance and Dynamic Dispatch Chapter 24
public interface Displaceable { public int getx(); public int gety(); public void move (int x, int y); public class Point implements Displaceable { private int x, y; public Point(int x0, int y0) { x = x0; y = y0; public int getx() { return x; public int gety() { return y; public void move(int dx, int dy) { x = x + dx; y = y + dy; What does this program do? public class Main { public static double dist(displaceable d) { int x = d.getx(); int y = d.gety(); return Math.sqrt(x * x + y * y); public static void main (String[] args) { Point p = new Point(3,4); System.out.println(dist(p)); 1. Nothing. It doesn't type check 2. It prints 5.0 on the console 3. NPE 4. Something else Answer: 2
Homework 7 due Tuesday Announcements Get the project set up on your laptop now, in case there are issues with Eclipse Exam: Great Job! view your exam with Ms. Fox starzng next Tuesday a[ernoon soluzons posted next week submit regrade requests by last day of classes Le]er grade ranges on Piazza post StaZsZcs: median: 87 mean: 83.34 stdev: 13.25 max: 100 (11)
Extension Sharing code between related types
Interfaces and Subtyping interfaces classes Types can have many different supertypes / subtypes Last Zme we saw interface extension: sharing code between interfaces Can we make it easier to share code in classes?
Class Extension Demo
Class Extension: Inheritance Classes, like interfaces, can also extend one another. Unlike interfaces, a class can extend only one other class. The extending class inherits all of the fields and methods of its superclass, and may include addizonal fields and methods. public class DisplaceableImpl implements Displaceable { private int x, y; public DisplaceableImpl(int x0, int y0) { this.x = x0; this.y = y0; public int getx() { return x; public int gety() { return y; public void move(int dx, int dy) { x = x + dx; y = y + dy;
Class Extension: Inheritance public class DisplaceableImpl implements Displaceable { private int x, y; public DisplaceableImpl(int x0, int y0) { this.x = x0; this.y = y0; public int getx() { return x; public class Circle extends DisplaceableImpl implements Displaceable, Area { private int radius; public Circle(int x0, int y0, int initradius) { super(x0,y0); radius = initradius; public double getarea () { return 3.14159 * radius * radius; Same extends keyword, different form of extension Constructors are not inherited
Simple Inheritance This is an example of simple inheritance, the subclass only adds new fields or methods Use simple inheritance to share common code among related classes Example: Point, Circle, and Rectangle have iden1cal behavior for getx(), gety(), and move() methods Inheritance captures the is a relazonship between objects (e.g. a Car is a Vehicle, a Circle is a DisplaceableThing) Class extension should never be used for the "has a" relazonship
Subtyping with Inheritance Interfaces Classes Displaceable Area DisplaceableImpl Shape Point Circle Rectangle Extends Implements - Type C is a subtype of D if D is reachable from C by following zero or more edges upwards in the hierarchy. - e.g. Circle is a subtype of Area, but Point is not
public class { boolean equals( o) { // test for equality String tostring() { // return a string representation // other methods omitted is the root of the class tree. Classes that leave off the extends clause implicitly extend Arrays also implement the methods of This class provides methods useful for all objects to support is the highest type in the subtyping hierarchy.
Subtyping classes (form a tree) interfaces Displaceable Area DisplaceableImpl Shape Point Circle Rectangle Extends Implements Subtype by fiat - Interfaces extend (possibly many) interfaces - Classes implement (possibly many) interfaces - Classes (except ) extend exactly one other class ( if implicit) - Interface types (and arrays) are subtypes by fiat of
Other forms of inheritance Java has other features related to inheritance (some of which we will discuss later in the course): A subclass might override (re- implement) a method already found in the superclass. A class might be abstract i.e. it does not provide implementazons for all of its methods (its subclasses must provide them instead) These features are hard to use properly and the need for them arises in special cases Special methods: equals and tostring Making reusable libraries We recommend avoiding all forms of inheritance (even simple inheritance ) when possible prefer interfaces and composizon. Especially avoid overriding.
The Java Abstract Stack Machine Dynamic Dispatch What code runs in a method call?
How do method calls work? What code gets run in a method invocazon? o.move(3,4); When that code is running, how does it access the fields of the object that invoked it? x = x + dx; What if the method was inherited from a superclass?
An Example public class { private int x; public () { x = 0; public void incby(int d) { x = x + d; public int get() { return x; public class extends { private int y; public (int inity) { y = inity; public void dec() { incby(-y); // somewhere in main: d = new (2); d.dec(); c = d; Answer: - 2 What result does this program calculate for x? 1. 1 2. 2 3. -2 4. 0 5. NullPointerException
ASM refinement: The Class Table Class Table public class { private int x; public () { x = 0; public void incby(int d) { x = x + d; public int get() { return x; public class extends { private int y; public (int inity) { y = inity; public void dec() { incby(-y); The class table contains: The code for each method, Back pointers to each class s parent, and The class s stazc members. String tostring(){ boolean equals extends () { x = 0; void incby(int d){ extends (int inity) {
with Explicit this and super public class extends { private int x; public () { super(); this.x = 0; public void incby(int d) { this.x = this.x + d; public int get() { return this.x; public class extends { private int y; public (int inity) { super(); this.y = inity; public void dec() { this.incby(-this.y); // somewhere in main: d = new (2); d.dec(); c = d;
ConstrucZng an Workspace Stack Heap d = new (2); d.dec(); c = d; Class Table String tostring(){ boolean equals Invoking a constructor: allocates space for a new object in the heap includes slots for all fields of all ancestors in the class tree (here: x and y) creates a pointer to the class this is the object s dynamic type runs the constructor body a[er pushing parameters and this onto the stack extends () { x = 0; void incby(int d){ extends (int inity) {
AllocaZng Space on the Heap Workspace Stack Heap Class Table super(); this.y = inity; d = _; d.dec(); c = d; this x 0 y 0 String tostring(){ boolean equals inity 2 Invoking a constructor: allocates space for a new object in the heap includes slots for all fields of all ancestors in the class tree (here: x and y) creates a pointer to the class this is the object s dynamic type runs the constructor body a[er pushing parameters and this onto the stack Reminder: fields start with a sensible default - 0 for numeric values - null for references extends () { x = 0; void incby(int d){ extends (int inity) {
Calling super Workspace Stack Heap Class Table super(); this.y = inity; d = _; d.dec(); c = d; this x 0 y 0 String tostring(){ boolean equals inity 2 extends Call to super: The constructor (implicitly) calls the super constructor Invoking a method/constructor pushes the saved workspace, the method params (none here) and a new this pointer. () { x = 0; void incby(int d){ extends (int inity) {
Abstract Stack Machine Workspace Stack Heap Class Table super(); this.x = 0; d = _; d.dec(); c = d; this x 0 y 0 String tostring(){ boolean equals inity 2 _; this.y = inity; this extends (Running s default constructor omi]ed.) () { x = 0; void incby(int d){ extends (int inity) {
Assigning to a Field Workspace Stack Heap Class Table this.x = 0; d = _; d.dec(); c = d; this x 0 y 0 String tostring(){ boolean equals inity 2 _; this.y = inity; this extends Assignment into the this.x field goes in two steps: - look up the value of this in the stack - write to the x slot of that object. () { x = 0; void incby(int d){ extends (int inity) {
Assigning to a Field Workspace Stack Heap Class Table.x = 0; d = _; d.dec(); c = d; this x 0 y 0 String tostring(){ boolean equals inity 2 _; this.y = inity; this extends Assignment into the this.x field goes in two steps: - look up the value of this in the stack - write to the x slot of that object. () { x = 0; void incby(int d){ extends (int inity) {
Done with the call Workspace Stack Heap Class Table ; d = _; d.dec(); c = d; this x 0 y 0 String tostring(){ boolean equals inity 2 _; this.y = inity; this extends Done with the call to super, so pop the stack to the previous workspace. () { x = 0; void incby(int d){ extends (int inity) {
ConZnuing Workspace Stack Heap Class Table this.y = inity; d = _; d.dec(); c = d; ; this x 0 y 0 String tostring(){ boolean equals inity 2 extends ConZnue in the class s constructor. () { x = 0; void incby(int d){ extends (int inity) {
Abstract Stack Machine Workspace Stack Heap Class Table this.y = 2; d = _; d.dec(); c = d; this x 0 y 0 String tostring(){ boolean equals inity 2 extends () { x = 0; void incby(int d){ extends (int inity) {
Assigning to a field Workspace Stack Heap Class Table this.y = 2; d = _; d.dec(); c = d; this x 0 y 2 String tostring(){ boolean equals inity 2 extends Assignment into the this.y field. (This really takes two steps as we saw earlier, but we re skipping some for the sake of brevity) () { x = 0; void incby(int d){ extends (int inity) {
Done with the call Workspace Stack Heap Class Table ; d = _; d.dec(); c = d; this x 0 y 2 String tostring(){ boolean equals inity 2 extends Done with the call to the constructor, so pop the stack and return to the saved workspace, returning the newly allocated object (now in the this pointer). () { x = 0; void incby(int d){ extends (int inity) {
Returning the Newly Constructed Workspace Stack Heap Class Table d = ; d.dec(); c = d; x 0 y 2 String tostring(){ boolean equals ConZnue execuzng the program. extends () { x = 0; void incby(int d){ extends (int inity) {
AllocaZng a local variable Workspace Stack Heap Class Table d.dec(); c = d; d x 0 y 2 String tostring(){ boolean equals extends Allocate a stack slot for the local variable d. Note that it s mutable (bold box in the diagram). Aside: since, by default, fields and local variables are mutable, we o[en omit the bold boxes and just assume the contents can be modified. () { x = 0; void incby(int d){ extends (int inity) {
Dynamic Dispatch: Finding the Code Workspace Stack Heap Class Table.dec(); c = d; d x 0 y 2 String tostring(){ boolean equals extends Invoke the dec method on the object. The code is found by pointer chasing through the class hierarchy. This process is called dynamic dispatch: Which code is run depends on the dynamic class of the object. (In this case,.) Search through the methods of the, class trying to find one called dec. () { x = 0; void incby(int d){ extends (int inity) {
Dynamic Dispatch: Finding the Code Workspace Stack Heap Class Table this.incby(-this.y); d _; c = d; this x 0 y 2 String tostring(){ boolean equals extends Call the method, remembering the current workspace and pushing the this pointer and any arguments (none in this case). () { x = 0; void incby(int d){ extends (int inity) {
Reading A Field s Contents Workspace Stack Heap Class Table this.incby(-.y); d _; c = d; this x 0 y 2 String tostring(){ boolean equals extends Read from the y slot of the object. () { x = 0; void incby(int d){ extends (int inity) {
Dynamic Dispatch, Again Workspace Stack Heap Class Table.incBy(-2); d _; c = d; this x 0 y 2 String tostring(){ boolean equals Invoke the incby method on the object via dynamic dispatch. In this case, the incby method is inherited from the parent, so dynamic dispatch must search up the class tree, looking for the implementazon code. The search is guaranteed to succeed Java s stazc type system ensures this. Search through the methods of the, class trying to find one called incby. If the search fails, recursively search the parent classes. extends () { x = 0; void incby(int d){ extends (int inity) {
Running the body of incby Workspace Stack Heap Class Table this.x = this.x + d; d this.x = -2; _; int x = d.get(); this _; x -20 y 2 String tostring(){ boolean equals d -2 extends this () { x = 0; It takes a few steps Body of incby: - reads this.x - looks up d - computes result this.x + d - stores the answer (- 2) in this.x void incby(int d){ extends (int inity) {
A[er a few more steps Workspace Stack Heap Class Table c = d; d x -2 String tostring(){ y 2 boolean equals extends () { x = 0; Now use dynamic dispatch to invoke the get method for d. This involves searching up the class hierarchy again void incby(int d){ extends (int inity) {
A[er a few more steps Workspace Stack Heap Class Table d c x -2 y 2 String tostring(){ boolean equals extends () { x = 0; Now use dynamic dispatch to invoke the get method for c. This involves searching up the class hierarchy again void incby(int d){ extends (int inity) {
A[er yet a few more steps Workspace Stack Heap Class Table ; d c x -2 String tostring(){ x -2 y 2 boolean equals extends () { x = 0; void incby(int d){ Done! (Phew!) extends (int inity) {
Summary: this and dynamic dispatch When object s method is invoked, as in o.m(), the code that runs is determined by o s dynamic class. The dynamic class, represented as a pointer into the class table, is included in the object structure in the heap If the method is inherited from a superclass, determining the code for m might require searching up the class hierarchy via pointers in the class table This process of dynamic dispatch is the heart of OOP! Once the code for m has been determined, a binding for this is pushed onto the stack. The this pointer is used to resolve field accesses and method invocazons inside the code.