Student: There’s something bothering me. In the Location example we talked about before, the location class had that method that returned Unknown, and then all the other classes overrode it. It looked like this:
Location
public String pinLabel() { return "Unknown Location"; }Prof: Right. All the subclasses had a
pinLabel method with @Override to give their own behavior.Student: Well, isn’t it weird that we have this code, but it’s not going to be used ever?
Prof: Well, what if we made a
Location directly?Student: I guess, but in all the examples we only made subclasses. Like we never used
new Location as a constructor. So it seems just silly that it even has this method.Prof: That’s an interesting observation! So you are wondering if we could just leave it out, because it gets implemented on all the subclasses? What happens if we do? That’s pretty testable. Here was the setup:
Student: Yeah, so it says it has to be there, because
Location implements Place.Prof: This is a great observation. What would help here is a special kind of class – a class that is not meant for creating on its own, but is meant for describing some fields and methods shared across many classes, and then some methods that need to be implemented by subclasses. Does that sound right?
Student: Yeah, that’s right
Prof: This is called an abstract class or an abstract base class. Here’s what it looks like:
Prof: There are a few interesting consequences of this. First,
Location being abstract means it’s a compile error to use new Location.Student: Really? A compile error? Then why have the constructor at all?
Prof: You can still call the constructor using
super from a subclass, so the code there is still relevant.Student: Hmm OK I get it. And so then the
abstract method doesn’t have a body?Prof: Yes, in that way it’s very similar to an interface. Actually, in this case since
Location declares that it implements Place, you could leave off the abstract method definition entirely, and it’d still compile and mean the same thing.Student: How does that work?
Prof: Java figures it out – since
pinLabel is part of the interface, that means any class that extends Location must implement it as well. So it counts the same as an abstract method.Student: Huh, interesting.
Practice
Here are social media posts with an abstract base class.
Message holds the fields every post shares and a snippet method they all use; each subclass gives its own preview. Write the body of each subclass constructor.interface Post {
String preview();
String snippet();
}
abstract class Message implements Post {
String username;
String message;
int likes;
Message(String username, String message, int likes) {
this.username = username;
this.message = message;
this.likes = likes;
}
public abstract String preview();
public String snippet() {
if (message.length() <= 10) { return "@" + username + ": " + message; }
return "@" + username + ": " + message.substring(0, 10) + "...";
}
}
class TextPost extends Message {
boolean edited;
TextPost(String username, String message, int likes, boolean edited) {
}
public String preview() {
String text = "@" + username + ": " + message;
if (edited) { text = text + " (edited)"; }
return text + " | " + likes + " likes";
}
}
class Repost extends Message {
String from;
Repost(String username, String message, int likes, String from) {
}
public String preview() {
return "@" + username + " reposted @" + from + ": " + message + " | " + likes + " likes";
}
}
class VideoPost extends Message {
int seconds;
VideoPost(String username, String message, int likes, int seconds) {
}
public String preview() {
return "@" + username + ": " + message + " [video, " + seconds + "s] | " + likes + " likes";
}
}
Post[] feed = {
new TextPost("cse11", "Exam moved to Friday", 12, true),
new Repost("ana", "Free pizza in the CSE lobby", 40, "tritons"),
new VideoPost("ben", "My cat learns Java", 7, 42),
};
String[] previews = { feed[0].preview(), feed[1].preview(), feed[2].preview() };
String[] snippets = { feed[0].snippet(), feed[1].snippet(), feed[2].snippet() };
String preview();
String snippet();
}
abstract class Message implements Post {
String username;
String message;
int likes;
Message(String username, String message, int likes) {
this.username = username;
this.message = message;
this.likes = likes;
}
public abstract String preview();
public String snippet() {
if (message.length() <= 10) { return "@" + username + ": " + message; }
return "@" + username + ": " + message.substring(0, 10) + "...";
}
}
class TextPost extends Message {
boolean edited;
TextPost(String username, String message, int likes, boolean edited) {
}
public String preview() {
String text = "@" + username + ": " + message;
if (edited) { text = text + " (edited)"; }
return text + " | " + likes + " likes";
}
}
class Repost extends Message {
String from;
Repost(String username, String message, int likes, String from) {
}
public String preview() {
return "@" + username + " reposted @" + from + ": " + message + " | " + likes + " likes";
}
}
class VideoPost extends Message {
int seconds;
VideoPost(String username, String message, int likes, int seconds) {
}
public String preview() {
return "@" + username + ": " + message + " [video, " + seconds + "s] | " + likes + " likes";
}
}
Post[] feed = {
new TextPost("cse11", "Exam moved to Friday", 12, true),
new Repost("ana", "Free pizza in the CSE lobby", 40, "tritons"),
new VideoPost("ben", "My cat learns Java", 7, 42),
};
String[] previews = { feed[0].preview(), feed[1].preview(), feed[2].preview() };
String[] snippets = { feed[0].snippet(), feed[1].snippet(), feed[2].snippet() };