Data abstraction and class design
A class is a decision about what an object remembers, who may change it, and what it can be asked to do. Everything in this area is that one decision seen from a different side: writing a class of your own, building one on top of another, replacing a method you inherited, reaching the part of yourself that the parent owns, and signing a promise that lets somebody else's code use your class without knowing anything about it.
What a class decides
Here is the smallest class worth looking at, and every line of it is a choice somebody made.
public class Student
{
private String name;
private double gpa;
public Student(String name, double gpa)
{
if (name == null)
{
throw new IllegalArgumentException("name cannot be null");
}
this.name = name;
this.gpa = gpa;
}
public String getName()
{
return this.name;
}
}
The fields are private, which means no code outside this file can put a value in them. That is not politeness. It is what makes the rest of the class worth anything: because the only ways in are the ones written here, the class can decide what a valid Student is and actually hold that line. A public field has no door, so nothing can be promised about it by anybody.
There is a getter for the name and no setter, and that absence is also a choice. A student's name does not change after the object is built, so there is a way to read it and no way to write it. If you ever added a setter, it would need the same null check the constructor has, because a guarantee about a field is only as strong as the weakest way into that field.
What a constructor promises
The check sits above both assignments, and the word that matters is "above". If the name is null, the throw happens while the object is still half built, and new hands back nothing at all. So the promise is not "most students have a name". It is: every Student object that exists anywhere in this program passed that check, because there is no other way one could have come into being.
That is the whole job of a constructor. Take the object from the defaults Java handed it, which are null for a reference and 0.0 for a number, to a state you are willing to stand behind, and refuse to finish if you cannot.
What extends gives a child, and what it does not
public class Dog extends Mammal
One word, and a Dog is now a kind of Mammal. It carries every field and method Mammal declares, and it can be used anywhere a Mammal is wanted. Two things do not come with it. Constructors are not inherited, so Dog writes its own and calls the parent's. And private does not open up: a field the parent declared private sits inside every Dog object and is invisible to Dog's code, because private belongs to a class rather than to a family.
Then there is the line that makes the rest of this area work.
Mammal m = new Dog("Scooby");
Two types, 2 jobs. The variable's type decides what you are allowed to call. The object's type decides which body actually runs. Java settles the first while compiling and the second at the moment of the call, by looking at what is really there.
Which method a call reaches
Every class already inherits a toString from Object, and that inherited one prints the class name and a number about where the object sits in memory. Writing your own replaces it for your objects, and Java calls it for you whenever it needs text from an object, inside println and on either side of a +.
Replacing it is worth the 5 minutes on any class. It is on the official list for a better reason than that, though, which is what an override proves you understand. A method in a parent can call a method the parent has no body for, and the call lands in whichever child is really there. That is how one sentence written once in Mammal prints WOOF for a dog and HELLO for a human without Mammal knowing either word.
What an interface promises
An interface is a list of method headers with no bodies. implements signs it, and the compiler holds you to it. In exchange, code that was written against the interface years before your class existed will work on your class.
Comparable is the smallest useful example and the one the official list names. Java knows how to put numbers and strings in order. It does not know how you want your objects arranged, and it will not guess: hand a sort an array of a class that never said, and it throws a ClassCastException at run time. Write compareTo and the same sort works, because the sort asks 1 question of each pair and your method is the answer.
What the department names
The official APE site lists the topics this area covers. Its own words:
Data Abstraction and Class Design
Extending a class
Overriding a method, such as toString
Creating a class
Accessing super class methods and/or fields
Implementing an interface, such as Comparable
Its page of problem areas from previous exams names the cost of not knowing the 2 keywords:
Unfamiliarity with keywords in Java required for basic things like inheritance (extends) and interface use (implements)
Every item on that list has a lesson of its own.
The lessons
Trace these before you go on
Work each one on paper and write the answer down before you open it. If one of them surprises you, start with the lesson it came from.
Mammal declares private String nickName; and a public toString. Dog extends Mammal.
- Can code inside
Dogwritethis.nickName = "Scooby";? - Does a
Dogobject contain anickNamefield at all? - Inside
Mammal, a method takes anotherMammalas a parameter and readsother.nickName. Does that compile?
Answers
1. No. Private belongs to the class that declared it, and Dog is different code in a different file.
2. Yes. The field is in every Dog object. Carrying a field and being allowed to read it are separate questions.
3. Yes. Private is private to the class, not to the object, so code inside Mammal can read any Mammal's fields, including one it was handed.
Mammal is abstract and declares communicate(). Dog returns WOOF and adds public String fetch(). The line is Mammal m = new Dog("Scooby");.
- Does
m.communicate()compile, and what does it return? - Does
m.fetch()compile? Is the failure at compile time or at run time? - Does
Mammal m2 = new Mammal("Scooby");compile?
Answers
1. It compiles, because the variable's type declares the method, and it returns WOOF, because the object is a Dog.
2. It does not compile. The failure is at compile time, decided by the variable's type alone, even though the object really does have the method.
3. No. Mammal is abstract, so there is no way to build a plain one anywhere in the program. That is exactly what declaring it abstract was for.
A file reads public class Mammal on its class line and holds public abstract String communicate(); inside it.
- Does it compile as written? If not, what is the smallest fix?
- Someone instead deletes the abstract method so the file compiles. What has that cost?
- With the class declared abstract,
Dog extends Mammalbut supplies nocommunicate. What stops working, and which class does the error name?
Answers
1. It does not compile. A class holding a method with no body cannot claim to be complete. The fix is 1 word on the class line, abstract.
2. Every subclass loses the requirement to say what it says, and toString in the parent has nothing to call. The file compiles and the design is gone.
3. new Dog("Scooby") stops compiling, because Dog inherited a promise it did not keep and is therefore abstract too. The error names Dog, not Mammal.
A class implements Comparable and its whole compareTo is return this.id - other.id;.
- With ids 12345 and 12340, what sign comes back, and is it right?
- With
this.idequal toInteger.MAX_VALUEandother.idequal to-1, what value comes back, and is it right? - Write the line that has neither problem.
- A different class's
compareToreturns-9. What does a sort do with the 9?
Answers
1. Positive, and it is right. Small numbers far from the edges of the range subtract without any trouble, which is what makes the habit survive testing.
2. -2147483648. The true answer is 1 more than the largest int, so it wraps around to the most negative one, and the method now claims the largest id in the range comes first. Nothing throws.
3. return Integer.compare(this.id, other.id); It never subtracts, so it cannot overflow.
4. Nothing. Only the sign is read. A negative number means this one comes first and the size of it carries no information at all.
The practice problems
Three worked problems from the department's practice exam, each with the reasoning and the trace. Each one is also the worked example inside a lesson, so start with the lesson if the problem does not click.
Where else this is taught on this site
Short pages, 1 idea each, for whichever piece is shaky.
- A Class Is State Plus the Rules That Guard It
- An Interface Is a Promise a Type Makes
- An Order Is a Sign, Not a Magnitude
- Natural Order Is implements Comparable
- Writing the compareTo Body: One Key, Then Several
- The Subtraction Trick and Why It Overflows
- The Fix: Integer.compare and Similar
- IS-A Is Inheritance
- Favor Composition over Inheritance
The one thing to keep
A class keeps its fields private so that its own code is the only code that can put a value in them, and the constructor is the door that decides which values get through. extends makes one class a kind of another and hands the child everything except the constructors and the private fields. The variable's type decides what you may call and the object's type decides which body runs, which is what makes an override useful and what lets a parent call a method it has no body for. implements is a promise the compiler checks, and compareTo is the promise that turns your class into something every sort in the library already knows how to arrange.