Student: When do I use inheritance?
Prof: There are a few different cases people talk about. One I think is important is sharing fields across classes.
Student: Sharing fields?
Prof: Let’s think about representing locations on a service like Google Maps. They might want to have a bunch of different classes for them, and an interface that ties them together. A heads up, we’re getting into some longer examples:
Student: Wow you weren’t kidding that it’s longer.
Prof: Well, we’re talking about some more substantial design questions, so our programs do need to grow a bit! What do you notice and wonder?
Student: Well, I notice that each of these classes
implements Place – there is no inheritance yet, because I don’t see extends.Prof: Good, good. Other things?
Student: I wonder what a Pin is.
Prof: Oh good question. That’s a word for a point on a virtual map. So like a little icon or shape that shows up. The example is meant to capture different kinds of places and the information they might show. For example, the restaurant has a star rating but the bus stop just names its bus route.
Student: I see. And I notice that classes are being used instead of records.
Prof: Right,
extends only works with classes, and we’ll see how it does help us a bit here. There is something shared across all the classes: do you see it?Student: Hmmm… I guess lat and lng are shared across all of them.
Prof: Right. And with what we have, can we do anything to avoid having all those copies of
lat and lng?Student: Can we put
lat and lng in one class, and then extend it from all of these?Prof: That’s right! So the base class might be like this, though we’d have one problem:
class Location implements Place {
double lat;
double lng;
Location(double lat, double lng) { this.lat = lat; this.lng = lng; }
public boolean onScreen(double south, double north, double west, double east) {
return lat >= south && lat <= north && lng >= west && lng <= east;
}
}
// Then BusStop, Restaurant, etc can extend LocationStudent: It says it implements
Place, but has no pinLabel?Prof: Yup. So we have to fill in some
pinLabel if we want this to compile.Student: What should it return?
Prof: It’s hard to say. At some point I’ll show you how to avoid having to choose, for now I’ll just fill something in:
class Location implements Place {
// ...
public String pinLabel() { return "Unknown Location"; }
}Student: OK, so then what do the other classes look like?
Prof: Well, they gain
extends and lose their lat/lng fields. But we have to decide what to do in the constructor. There is a special keyword, super(...) that we’ll need to use. In a constructor it calls the parent class constructor with the given arguments. It lets us re-use the constructor from the class we inherited.Prof: So we’ve moved the idea of a location out of all these places, and made that be the job of
Location, which can also have the correct implementation of onScreen shared by all the subclasses. Now each of the subclasses is solely responsible for the implementation of pinLabel.Student: Interesting!
Practice
Here is a social media feed, already refactored so
Message holds the message field that every post shares. TextPost and Repost are done. Fill in VideoPost the same way.interface Post {
String preview();
boolean mentions(String word);
}
class Message implements Post {
String message;
Message(String message) {
this.message = message;
}
public String preview() { return message; }
public boolean mentions(String word) { return message.contains(word); }
}
class TextPost extends Message {
boolean edited;
TextPost(String message, boolean edited) {
super(message);
this.edited = edited;
}
@Override
public String preview() {
if (edited) { return message + " (edited)"; }
return message;
}
}
class Repost extends Message {
String from;
Repost(String message, String from) {
super(message);
this.from = from;
}
@Override
public String preview() { return "Reposted from " + from + ": " + message; }
}
class VideoPost {
int seconds;
VideoPost(String message, int seconds) {
this.seconds = seconds;
}
@Override
public String preview() { return message + " [video, " + seconds + "s]"; }
}
Post[] feed = {
new TextPost("Exam moved to Friday", true),
new Repost("Free pizza in the CSE lobby", "tritons"),
new VideoPost("My cat learns Java", 42),
};
String third = feed[2].preview();
boolean aboutCats = feed[2].mentions("cat");
String preview();
boolean mentions(String word);
}
class Message implements Post {
String message;
Message(String message) {
this.message = message;
}
public String preview() { return message; }
public boolean mentions(String word) { return message.contains(word); }
}
class TextPost extends Message {
boolean edited;
TextPost(String message, boolean edited) {
super(message);
this.edited = edited;
}
@Override
public String preview() {
if (edited) { return message + " (edited)"; }
return message;
}
}
class Repost extends Message {
String from;
Repost(String message, String from) {
super(message);
this.from = from;
}
@Override
public String preview() { return "Reposted from " + from + ": " + message; }
}
class VideoPost {
int seconds;
VideoPost(String message, int seconds) {
this.seconds = seconds;
}
@Override
public String preview() { return message + " [video, " + seconds + "s]"; }
}
Post[] feed = {
new TextPost("Exam moved to Friday", true),
new Repost("Free pizza in the CSE lobby", "tritons"),
new VideoPost("My cat learns Java", 42),
};
String third = feed[2].preview();
boolean aboutCats = feed[2].mentions("cat");