Computer Science 15-100, Summer 2009
Class Notes: Writing Classes
- Sample Code
- Classes Lexicon
- Instance Variables
- Instance Variables + Scope
- Constants + "final"
-
Initialization + Default Values (null, 0, false)
- Shadowing + "this"
- Instance Methods
- Methods Lexicon
- Method Overloading
- For Type-Specific
Processing
- For Default Values
- Variable-Length
Parameter Lists
- Constructors
- Default Constructor
- Custom
(Non-Default) Constructor
-
Custom (Non-Default) Constructor -> No Default Constructor
- Multiple Constructors
- Duplicating Code
- Sharing
Code with "this" constructor
- Sharing Code
with "init" Method
- Encapsulation (Data
Hiding)
- Accessors
(getters -- safely get state)
- Mutators
(setters -- safely set state)
- Object Methods
- toString
- equals
- hashCode
- Static Members
- Static Constants
- Static Variables
- Example: Random
instance
- Example:
Counting instances
- Static Methods
- Scope of Static
and Instance Members
- Arrays of Objects
-
Sortable Objects
(Comparable + compareTo)
-
New
class instances are not inherently "sortable"
-
"Sortable"
== Implements Comparable Interface
Writing Classes
- Sample Code
See Getting
Started with Writing Classes
- Classes Lexicon
Fields == Variables (and Constants) == Data == State == Properties ==
Attributes
Methods == Functions == Procedures == Commands == Behaviors == Operations
Class Members == Fields + Methods
API == Application Programming Interface == Public Methods
Class == API (Public Methods) + Private State + Private Helper Methods
Object == Instance
- Instance Variables
- Instance Variables + Scope
class Demo {
private int x = 5;
public void foo() {
System.out.println("foo can see that x = " +
x);
}
public void ack() {
x++;
System.out.println("ack just changed x to " +
x);
}
public void bar() {
System.out.println("bar can also see that x = " +
x);
}
public static void main(String[] args) {
Demo d = new Demo();
d.foo();
d.ack();
d.bar();
}
}
- Constants + "final"
public static
final int SOME_CONSTANT =
5;
-
Initialization + Default Values (null, 0, false)
class Demo {
private int x;
// uninitialized, assigned
default value
private String s; // ditto
private boolean b; // ditto
private int y = 42;
public Demo() {
System.out.println("Instance variables with default
values:");
System.out.println(" x = " + x);
System.out.println(" s = " + s);
System.out.println(" b = " + b);
System.out.println("And an initialized instance variable:");
System.out.println(" y = " + y);
}
public static void main(String[] args) {
new Demo();
}
}
- Shadowing + "this"
class Demo {
private int x = 5;
public void foo(int x) {
this.x += x;
}
public void printX() {
System.out.println("x = " + x);
}
public static void main(String[] args) {
Demo d = new Demo();
d.printX();
d.foo(10);
d.printX();
}
}
- Instance Methods
- Methods Lexicon
Method Signature == Method Name + Formal Parameters
Method Header == Visibility + Type + Signature (Method Name + Formal
Parameters)
Method == Method Declaration == Method Header + Body
Method Call == Method Invocation == Object Reference + "." + Method Name
+ Actual Parameters (or Arguments)
- Method Overloading
- For Type-Specific
Processing
class Demo {
private int x = 5;
public void printX() {
System.out.println("x = " + x);
}
public void add(int dx) {
System.out.println("adding int dx = " + dx);
x += dx;
}
public void add(String s)
{
System.out.println("adding String s = " + s);
add(Integer.parseInt(s));
}
public static void main(String[] args) {
Demo d = new Demo();
d.printX();
d.add(10);
d.printX();
d.add("42");
d.printX();
}
}
- For Default Values
class Demo {
private int x = 5;
private static final int DEFAULT_ADD_VALUE = 42;
public void printX() {
System.out.println("x = " + x);
}
public void add(int dx) {
System.out.println("adding int dx = " + dx);
x += dx;
}
public void add() {
System.out.println("adding default value = " +
DEFAULT_ADD_VALUE);
add(DEFAULT_ADD_VALUE);
}
public static void main(String[] args) {
Demo d = new Demo();
d.printX();
d.add(10);
d.printX();
d.add();
d.printX();
}
}
- Variable-Length
Parameter Lists
import
java.util.Arrays;
class Demo {
private int x = 5;
public void printX() {
System.out.println("x = " + x);
}
public void add(int dx) {
System.out.println("adding int dx = " + dx);
x += dx;
}
public void add(int... a)
{
System.out.println("adding these values: " +
Arrays.toString(a));
for (int i=0; i<a.length; i++)
add(a[i]);
}
public static void main(String[] args) {
Demo d = new Demo();
d.printX();
d.add(10);
d.printX();
d.add(2,4,6);
d.printX();
}
}
- Constructors
- Default Constructor
class Demo {
private int x = 5;
public void printX() {
System.out.println("x = " + x);
}
public static void main(String[] args) {
Demo d = new Demo();
System.out.println("Default constructor called!");
d.printX();
}
}
Which is basically the same as:
class Demo {
private int x = 5;
public void printX() {
System.out.println("x = " + x);
}
public Demo() {
}
public static void main(String[] args) {
Demo d = new Demo();
System.out.println("Explicit (non-default) constructor
called!");
d.printX();
}
}
- Custom
(Non-Default) Constructor
class Demo {
private int x = 5;
public void printX() {
System.out.println("x = " + x);
}
public Demo(int x) {
this.x = x;
}
public static void main(String[] args) {
Demo d = new Demo(42);
d.printX();
}
}
-
Custom (Non-Default) Constructor -> No Default Constructor
class Demo {
private int x = 5;
public void printX() {
System.out.println("x = " + x);
}
public Demo(int x) {
this.x = x;
}
public static void main(String[] args) {
Demo d = new Demo();
// will not compile!
d.printX();
}
}
- Multiple Constructors
- Duplicating Code
class Demo {
private int x;
public void printX() {
System.out.println("x = " + x);
}
public Demo(int x) {
System.out.println("Constructor: x = " + x);
this.x = x;
}
public Demo() {
System.out.println("Constructor: no parameter, setting x =
42");
this.x = 42;
}
public static void main(String[] args) {
Demo d1 = new Demo();
d1.printX();
Demo d2 = new Demo(999);
d2.printX();
}
}
- Sharing Code
with "this" constructor
class Demo {
private int x;
public void printX() {
System.out.println("x = " + x);
}
public Demo(int x) {
System.out.println("Constructor: x = " + x);
this.x = x;
}
public Demo() {
this(42);
}
public static void main(String[] args) {
Demo d1 = new Demo();
d1.printX();
Demo d2 = new Demo(999);
d2.printX();
}
}
But be sure "this" call is on first line!
public
Demo() {
System.out.println("Call other constructor");
// will not compile!
this(42);
}
- Sharing Code with
"init" Method
class Demo {
private int x;
public void printX() {
System.out.println("x = " + x);
}
public Demo(int x) {
System.out.println("Constructor: x = " + x);
init(x);
}
public Demo() {
System.out.println("Constructor: no parameter, setting x =
42");
init(42);
}
private void init(int x) {
System.out.println("In init, setting x = " + x);
this.x = x;
}
public static void main(String[] args) {
Demo d1 = new Demo();
d1.printX();
Demo d2 = new Demo(999);
d2.printX();
}
}
- Encapsulation (Data Hiding)
- Accessors
(getters -- safely get state)
class Demo {
private int[] a;
public Demo() {
a = null;
}
public Demo(int... a) {
this.a = a;
}
// accessor (safely gets length,
even if a is null)
public int getLength() {
return ((a == null) ? 0 : a.length);
}
public static void main(String[] args) {
Demo d1 = new Demo();
System.out.println(d1.getLength());
Demo d2 = new Demo(1,2,3);
System.out.println(d2.getLength());
}
}
- Mutators
(setters -- safely set state)
class Demo {
private double x, y;
private double d; // d is
distance from (0,0) to (x,y)
public Demo(double x, double y) {
this.x = x;
this.y = y;
setDistance();
}
private void setDistance() {
this.d = Math.sqrt(Math.pow(this.x,2) + Math.pow(this.y,2));
}
// mutator (safely maintains "d"
when setting "x")
public void setX(double x) {
this.x = x;
setDistance();
}
public String toString() {
return "Demo(x=" + x + ",y=" + y + ",d=" + d + ")";
}
public static void main(String[] args) {
Demo d = new Demo(5,12);
System.out.println(d);
d.setX(35);
System.out.println(d);
}
}
- Object Methods
- toString
class Demo {
public static void main(String[] args) {
System.out.println("With no toString method:");
System.out.println(new Pair1(1,2));
System.out.println("With a not-very-good toString method:");
System.out.println(new Pair2(1,2));
System.out.println("With a better toString method:");
System.out.println(new Pair3(1,2));
System.out.println("Or perhaps...");
System.out.println(new Pair4(1,2));
}
}
class Pair1 {
private int x, y;
public Pair1(int x, int y) { this.x = x; this.y = y; }
}
class Pair2 {
private int x, y;
public Pair2(int x, int y) { this.x = x; this.y = y; }
public String toString() {
return x + "," + y;
}
}
class Pair3 {
private int x, y;
public Pair3(int x, int y) { this.x = x; this.y = y; }
public String toString() {
return "Pair3(" + x + "," + y + ")";
}
}
class Pair4 {
private int x, y;
public Pair4(int x, int y) { this.x = x; this.y = y; }
public String toString() {
return "Pair4(x=" + x + ",y=" + y + ")";
}
}
- equals
class Demo {
public static void main(String[] args) {
System.out.print("With no equals method: ");
Pair1 p1a = new Pair1(1,2);
Pair1 p1b = new Pair1(1,2);
System.out.println(p1a.equals(p1b));
System.out.print("With an equals method: ");
Pair2 p2a = new Pair2(1,2);
Pair2 p2b = new Pair2(1,2);
System.out.println(p2a.equals(p2b));
// Also show that unlike objects and null work, too
System.out.println(p2a.equals("some string"));
System.out.println(p2a.equals(null));
}
}
class Pair1 {
private int x, y;
public Pair1(int x, int y) { this.x = x; this.y = y; }
}
class Pair2 {
private int x, y;
public Pair2(int x, int y) { this.x = x; this.y = y; }
public boolean equals(Object
object) {
if (!(object instanceof Pair2) || (object == null))
return false;
Pair2 that = (Pair2)
object;
return ((this.x == that.x) &&
(this.y ==
that.y));
}
}
- hashCode
// Simple
"good-enough" general-purpose hashCode implementation
public int hashCode() {
int code = 1;
Object[] state = { array of
instance variables };
for (int i=0; i<state.length; i++)
code = 31*code + ((state[i] == null) ? 0 : state[i].hashCode());
return code;
}
And a compelling example:
import java.util.HashSet;
class Demo {
public static void main(String[] args) {
System.out.println("To show why we need to write our own
hashCode");
System.out.println("method when we write our own equals
method,");
System.out.println("this demo uses a HashSet, which we have
not");
System.out.println("yet learned about. Ask your
instructor...");
System.out.println();
System.out.print("With no hashCode method: ");
Pair1 p1a = new Pair1(1,2);
Pair1 p1b = new Pair1(1,2);
HashSet<Pair1> set1 = new HashSet<Pair1>();
set1.add(p1a);
System.out.println(set1.contains(p1b));
System.out.print("With a hashCode method: ");
Pair2 p2a = new Pair2(1,2);
Pair2 p2b = new Pair2(1,2);
HashSet<Pair2> set2 = new HashSet<Pair2>();
set2.add(p2a);
System.out.println(set2.contains(p2b));
}
}
class Pair1 {
private int x, y;
public Pair1(int x, int y) { this.x = x; this.y = y; }
public boolean equals(Object object) {
Pair1 that = (Pair1) object;
return ((this.x == that.x) && (this.y == that.y));
}
}
class Pair2 {
private int x, y;
public Pair2(int x, int y) { this.x = x; this.y = y; }
public boolean equals(Object object) {
Pair2 that = (Pair2) object;
return ((this.x == that.x) && (this.y == that.y));
}
// Simple "good-enough" general-purpose hashCode implementation
public int hashCode() {
int code = 1;
Object[] state = { x,
y };
for (int i=0; i<state.length; i++)
code = 31*code + ((state[i] == null) ? 0 :
state[i].hashCode());
return code;
}
}
- Static Members
- Static Constants
public final
static int CLUBS = 1;
public final static int DIAMONDS = 2;
public final static int HEARTS = 3;
public final static int SPADES = 4;
- Static Variables
- Example: Random
instance
// Create one
shared random instance, rather than
// needlessly creating one-per-instance
public static Random random = new Random();
- Example: Counting
instances
class Demo {
private static int instances = 0;
public Demo(String name) {
Demo.instances++;
System.out.println("Creating instance #" + instances +
" (name=" + name + ")");
}
public static void main(String[] args) {
new Demo("fred");
new Demo("wilma");
new Demo("barney");
}
}
Note that this compiles without the "Demo." qualifier in the
constructor:
class Demo {
private static int instances = 0;
public Demo(String name) {
instances++;
System.out.println("Creating instance #" + instances +
" (name=" + name + ")");
}
public static void main(String[] args) {
new Demo("fred");
new Demo("wilma");
new Demo("barney");
}
}
- Static Methods
class Demo {
private String s;
public Demo(String s) { this.s = s; }
public void add(char c) {
s += c;
}
public int getCharCount(char c) {
return getCharCount(s, c);
}
// This helper method is private
because it is not part of the API.
// It is static because it does
not require an object reference.
private static int getCharCount(String s, char c) {
if (s == null) return 0;
int count = 1;
for (int i=0; i<s.length(); i++)
if (s.charAt(i) == c)
count++;
return count;
}
public static void main(String[] args) {
Demo d = new Demo("abacab");
System.out.println(d.getCharCount('b'));
d.add('b');
System.out.println(d.getCharCount('b'));
}
}
- Scope of Static
and Instance Members
- Instance Members Can See Static Members
class Demo {
private static int x = 5;
private int w = 3;
private static void g() {
System.out.println("In static method g....");
System.out.println(" can see static variable x = " + x);
}
private void f() {
System.out.println("In instance method f....");
System.out.println(" can see instance variable w = " + w);
System.out.println(" can see static variable x = " + x);
System.out.println(" and can call static method g....");
g();
}
public static void main(String[] args) {
Demo d = new Demo();
d.f();
}
}
- Static Members Cannot See Instance Members
class Demo {
private static int x = 5;
private int w = 3;
private static void g() {
System.out.println("In static method g....");
System.out.println(" can see static variable x = " + x);
// static methods cannot see instance methods
f(); // will
not compile
// and static methods cannot see instance variables
System.out.println(w);
// will not compile
}
private void f() {
System.out.println("In instance method f....");
System.out.println(" can see instance variable w = " + w);
System.out.println(" can see static variable x = " + x);
System.out.println(" and can call static method g....");
g();
}
public static void main(String[] args) {
Demo d = new Demo();
d.f();
}
}
- Arrays of Objects
import java.util.Arrays;
class Demo {
public static void main(String[] args) {
// Declare and
allocate array
Pair[] pairs = new Pair[5];
// Confirm that it is
initialized with null values
System.out.println(Arrays.toString(pairs));
// Now load the array
with Pair instances
for (int i=0; i<pairs.length; i++)
pairs[i] = new Pair(i,10*i);
// And print the values out again
System.out.println(Arrays.toString(pairs));
// Now use a
statically-allocated array of Pairs:
pairs = new Pair[]{ new Pair(42, 43), new Pair(44, 45) };
// And print the values out yet again
System.out.println(Arrays.toString(pairs));
}
}
class Pair {
private int x, y;
public Pair(int x, int y) { this.x = x; this.y = y; }
public String toString() {
return "Pair(x=" + x + ",y=" + y + ")";
}
}
-
Sortable Objects
(Comparable + compareTo)
-
New class
instances are not inherently "sortable"
import java.util.*;
class Demo {
public static void main(String[] args) {
// Declare, allocate, and load array with (somewhat) random
pairs
Random random = new Random();
Pair[] pairs = new Pair[10];
for (int i=0; i<pairs.length; i++)
pairs[i] = new Pair(i%3, random.nextInt(100));
// And print the unsorted values
System.out.println(Arrays.toString(pairs));
// Now sort them
Arrays.sort(pairs);
// compiles but does not run
correctly.
// Then again, how could it?
How could Java know
// how to sort an array of Pair
instances?
// And print the sorted values
System.out.println(Arrays.toString(pairs));
}
}
class Pair {
private int x, y;
public Pair(int x, int y) { this.x = x; this.y = y; }
public String toString() {
// abbreviated for this example
return "(" + x + "," + y + ")";
}
}
-
"Sortable" ==
Implements Comparable Interface
import java.util.*;
class Demo {
public static void main(String[] args) {
// Declare, allocate, and load array with (somewhat) random
pairs
Random random = new Random();
Pair[] pairs = new Pair[10];
for (int i=0; i<pairs.length; i++)
pairs[i] = new Pair(i%3, random.nextInt(100));
// And print the unsorted values
System.out.println(Arrays.toString(pairs));
// Now sort them
Arrays.sort(pairs);
// And print the sorted values
System.out.println(Arrays.toString(pairs));
}
}
class Pair implements Comparable
{
private int x, y;
public Pair(int x, int y) { this.x = x; this.y = y; }
public String toString() {
// abbreviated for this example
return "(" + x + "," + y + ")";
}
// Must specify how to compare two Pair instances
public int compareTo(Object
object) {
Pair that = (Pair)
object;
// Let's sort by x
first, and in a tie, then by y
if (this.x != that.x)
return (this.x - that.x);
else
return (this.y - that.y);
}
}
carpe diem -
carpe diem - carpe diem - carpe diem
- carpe diem - carpe diem -
carpe diem - carpe diem - carpe
diem