// Student.java
/*
Demonstrates the most basic features of a class.
A student is defined by their current number of units.
There are standard get/set accessors for units.
The student responds to getStress() to report
their current stress level which is a function
of their units.
*/
public class Student extends Object {
// NOTE this is an "instance variable" named "units"
// Every Student object will have its own units variable.
// "protected" and "private" mean that clients do not get access
protected int units; // "protected" means access can be in the package
/* NOTE
"public static final" declares a public readable constant that
is associated with the class -- it's full name is Student.MAX_UNITS.
It's a convention to put constants like that in upper case.
*/
public static final int MAX_UNITS = 20;
public static final int DEFAULT_UNITS = 15;
// Constructs a student with the given units.
public Student(int initUnits) {
units = initUnits;
// NOTE this is example of "Receiver Relative" coding --
// "units" refers to the ivar of the receiver object.
// OOP code is written relative to an implicitly present receiver.
}
// Default constructor (no arguments).
// Inits with a default value of 15 units.
public Student() {
// "this" here is a special syntax to call one constructor
// from another -- it must be the first line.
this(DEFAULT_UNITS);
}
// Gets the current units value (standard "getter" accessor).
public int getUnits() {
return units;
}
// Sets the units, unless the new value would fall outside
// the range 0..MAX_UNITS.
public void setUnits(int units) {
if ((units < 0) || (units > MAX_UNITS)) {
return;
// Could use a number of strategies here: throw an
// exception, print to stderr, return false
}
this.units = units;
// NOTE: "this.units" trick needed here since param and ivar
// are both called "units". Perhaps "newUnits" would be a better
// name, but we wanted to show the this.unit syntax.
}
/*
Gets the current stress of the student
(defined as units *10).
NOTE another example of "Receiver Relative" coding
*/
public int getStress() {
return(units * 10);
}
/*
Tries to drop the given number of units.
Does not drop if would go below 9 units.
Returns true if the drop succeeds.
*/
public boolean dropUnits(int drop) {
if (units-drop >= 9) {
setUnits(units - drop); // NOTE send self a message
return true;
}
return false;
}
/*
In main() we have some typical looking client-of-Student code.
NOTE Invoking "java Student" from the command line runs this.
It's handy to put test/demo/sample client code in the main() of a class.
*/
public static void main(String[] args) {
// Make two students
Student a = new Student(12); // new 12 unit student
Student b = new Student(); // new 15 unit student (default ctor)
// They respond to getUnits() and getStress()
System.out.println("a u:" + a.getUnits() + " s:" + a.getStress());
System.out.println("b u:" + b.getUnits() + " s:" + b.getStress());
System.out.println("a drops 3 units");
a.dropUnits(3); // a drops a class
System.out.println("a u:" + a.getUnits() + " s:" + a.getStress());
// Now "b" points to the same object as "a" (pointer copy)
b = a;
b.setUnits(10);
System.out.println("b = a, b.setUnits(10)");
// So the "a" units have been changed
System.out.println("a u:" + a.getUnits() + " s:" + a.getStress());
// NOTE: public vs. private
// A statement like "b.units = 10;" will not compile in a client
// of the Student class when units is declared protected or private
/*
OUTPUT...
a u:12 s:120
b u:15 s:150
a drop 3 units
a u:9 s:90
b = a, b.setUnits(10)
a u:10 s:100
*/
}
}
/*
Things to notice...
-Demonstrates the Object-lifecycle -- clients create the object with new
(must go through constructor), then send it messages. Hard for the client
to mess up the state of the object. Note how setUnits() can maintain the
internal correctness of the object.
-The implementation code can refer to instance variables like "units"
by name. It automatically binds to the ivar of the receiver.
-"units" is declared protected. Thereore, a client cannot write something like
"a.units++;". The client must go through public messages like setUnits().
This promotes a less fragile design. The client may access things declared
"public".
-State vs. Computation -- notice that the client can't really tell if stress is
stored or computed. It just appears to be a property that Students have. Whether
it is stored or computed is just a detail. This is a nice separation between
the abstraction exposed by client and how it is actually implemented.
*/
------------------------------------------------------
Another grad.java class inherited from student.java, add thesis
// Grad.java
/*
Grad is a subclass of Student -- a simple example of subclassing.
-adds the state of yearsOnThesis
-overrides getStress() to provide a Grad specific version
*/
public class Grad extends Student {
private int yearsOnThesis;
/*
Ctor takes an initial units and initial years on thesis.
*/
public Grad(int units, int thesis) {
// we use "super" to invoke the superclass ctor
// to init that part of ourselves
super(units);
// init our own ivars
yearsOnThesis = thesis;
}
/*
Default ctor builds a Grad with 10 units and 0 yot.
*/
public Grad() {
this(10, 0); // "this" on first line of a ctor calls
// a different ctor in the same class
}
/*
getStress() override
Grad stress is 2 * Student stress + yearsOnThesis.
*/
@Override
public int getStress() {
// Use super.getStress() to invoke the Student
// version of getStress() instead of copy/pasting that
// code down here. The whole point of inheritance
// is not duplicating code.
int stress = super.getStress();
return(stress*2 + yearsOnThesis);
}
// Standard accessors
public void setYearsOnThesis(int yearsOnThesis) {
this.yearsOnThesis = yearsOnThesis;
}
public int getYearsOnThesis() {
return(yearsOnThesis);
}
// standard Hashcode auto-generated by Eclipse using the "source" menu
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + yearsOnThesis;
return result;
}
// standard Equals auto-generated by Eclipse using the "source" menu
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
Grad other = (Grad) obj;
if (yearsOnThesis != other.yearsOnThesis)
return false;
return true;
}
/*
Example client code of Student and Grad, demonstrating
inheritance concepts.
*/
public static void main(String[] args) {
Student s = new Student(13);
Grad g = new Grad(13, 2);
Student x = null;
System.out.println("s " + s.getStress());
System.out.println("g " + g.getStress());
// Note how g responds to everything s responds to
// with a combination of inheritance and overriding...
s.dropUnits(3);
g.dropUnits(3);
System.out.println("s " + s.getStress());
System.out.println("g " + g.getStress());
/*
OUTPUT...
s 130
g 262
s 100
g 202
*/
// s.getYearsOnThesis(); // NO does not compile
g.getYearsOnThesis(); // ok
// Substitution rule -- subclass may play the role of superclass
x = g; // ok
// At runtime, this goes to Grad.getStress()
// Point: message/method resolution uses the RT class of the receiver,
// not the CT class in the source code.
// This is essentially the objects-know-their-class rule at work.
x.getStress(); // returns 202
// g = x; // NO -- does not compile,
// substitution does not work that direction
// x.getYearsOnThesis(); // NO, does not compile
((Grad)x).getYearsOnThesis(); // insert downcast
// Ok, so long as x really does point to a Grad at runtime
}
@Override
public String toString() {
return "Grad(units=" + units + "; years=" + yearsOnThesis + ")";
}
}
/*
Things to notice...
-The ctor takes both Student and Grad state -- the Student state is passed up
to the Student ctor by the first "super" line in the Grad ctor.
-getStress() is a classic override. Note that it does not _repeat_ the code
from Student.getStress(). It calls it using super, and fixes the result.
The whole point of inheritance is to avoid code repetition.
-Grad responds to every message that a Student responds to -- either
a) inherited such as getUnits()
b) overridden such as getStress()
-Grad also responds to things that Students do not,
such as getYearsOnThesis().
*/
Source from http://www.stanford.edu/class/cs108/
No comments:
Post a Comment