public class Stack extends Object { int top; Object[] items; public Stack(int size) { this.top = 0; this.items = new Object[size]; } public void push(Object obj) { this.items[top] = obj; top++; } public Object pop() { this.top = this.top - 1; return this.items[top]; } }Observations: It looks a lot like C++. All instance methods are virtual. "this" is like "self" in Smalltalk. It's optional when talking about instance variables, but some people consider it good form... By default, every class inherits from Object. Arrays are bounds-checked. Ie. push and pop will raise exceptions at runtime if we access the items array out-of-bounds.
import java.util.*; public class Stack extends Object { ArrayList items; // defined in the java.util package public Stack() { items = new ArrayList(); // growable array object } public void push(Object obj) { items.add(obj); } public Object pop() { return(items.remove(items.size() - 1)); } // We override the toString method so that we can print this // object out nicely. public String toString() { return "Stack(" + items + ")"; // + is overloaded for Strings!! } // Every class can have a "main" method. It's a good place to // put test code... public static void main(String[] args) { Stack test = new Stack(); test.push("hello"); test.push("goodbye"); test.push(new Integer(17)); System.out.println(test); test.pop(); test.pop(); System.out.println(test); } }The above makes use of the "standard" class ArrayList to implement our Stack class. A large (over 3000 classes today) hierarchy of standard classes ships with the Java development kit. Understanding when/how to make use of these classes is a large part of becoming a fluent/effective Java programmer. The JDK also ships with a tool that automatically generates nice-looking HTML to document its source code. This documentation is then presented in a browser in a manner eerily similar to the ClassBrowser in Smalltalk.
import java.util.*; public class Thing {} public class Cheese extends Thing {} public abstract class Animal extends Thing { protected int weight; public int getWeight() { return weight; } public void setWeight(int pounds) { weight = pounds; } public abstract String getSound(); public abstract void eat(Thing a); } public class Cat extends Animal { protected ArrayList stomach; public Cat() { stomach = new ArrayList(); } public String getSound() { return "Meow"; } public void eat(Thing a) { if (! (a instanceof Cat)) { stomach.add(a); } } } public class CanibalCat extends Cat { public void eat(Thing a) { // I eat everything stomach.add(a); } } public class Mouse extends Animal { boolean hasCheese = false; public String getSound() { return "Squeak"; } public void eat(Thing a) { // I only eat cheese if (a instanceof Cheese) { hasCheese = true; } } }Draw the class hierarchy for the above. A client program might look like this:
import java.util.*; public class AnimalFarm { public static void main(String[] args) { ArrayList things = new ArrayList(); things.add(new CanibalCat()); things.add(new Cat()); things.add(new Mouse()); // so on and so forth... } }Notice that all instance methods are "virtual", that is the method that is invoked is based on the runtime class of the receiver object. Above we also see an example of a static method. Static methods are like class methods in Smalltalk. New objects are created using "new" as in C++, but there are important differences: the objects are garbage collected when no longer reached, and there is no crazy pointer syntax. Names (such as "a3") are references to objects. Assignment just changes pointers. There is no need to deal with nasty things like copy constructors or destructors in Java.
class Point { int x, y; public Point(int x, int y) { this.x = x; this.y = y; } public Point add(Point other) { return new Point(x + other.x, y + other.y); } public Point add(Point p1, Point p2) { return add(p1.add(p2)); } }Java, unlike C++, doesn't allow overloading of operators. In overloading, the compiler must decide statically (at compile time) which version of the method will be invoked. (Very different from the dynamic dispatch of inherited/overriden methods!)
class MouseEvent { /* a Mouse Event class... */ } public interface MouseListener { public void mousePressed(MouseEvent e); public void mouseReleased(MouseEvent e); // ... } class Frame { /* A base UI component... */ } class SomeUIComponent extends Frame implements MouseListener { // ... public void mousePressed(MouseEvent e) { // actually handles the mouse event here ... } public void mouseReleased(MouseEvent e) { // actually handles the mouse event here ... } }An interface is like an abstract class where every method is abstract. However, abstract classes typically provide some implementation. Interfaces are pure abstraction. A common (but somewhat extreme) approach says that there should be interfaces for all major classes in your system, for example:
class Color { /* a color class */ } interface Shape { public double area(); // every shape has an area public Color color(); // every shape has a color // etc } abstract class ShapeImpl { // fills in some details shared by all concrete shapes, such as color Color color; public Color color() { return color; } } // concrete classes must implement area!!! class Rectangle extends ShapeImpl { int width, height; double area() { return width * height; } } class Circle extends ShapeImpl { int radius; double area() { return radius * radius * Math.PI; } }
Each class defines a type. Hence, a subclass is always a subtype of its superclass(es) sine instances of the subclass can be used wherever instances of the superclass are expected:
Animal a1 = new Cat(); // ok Food f1 = new Cheese(); // ok Animal a2 = f1; // Error at compile time. Animal a3 = (Cat)f1; // Error at compile time. It's just not true! Cat c1 = (Cat)a1; // ok, but checked at runtimeThe rhs of an assignment can be a subtype of the lhs. The same is true for arguments/results to/from methods. Arguments can be subtypes of the method's parameters. The result may be a subtype of what's expected by the context.
// Cat inherits eat(Thing t) from Animal and overloads that // definition with eat(Mouse m). Example: class Cat extends Animal { public void eat(Mouse m) { ... } } ... Mouse m = new Mouse(); Cat c = new Cat(); c.eat(m); // which eat gets called?
class Point { /* A normal 2D point class */ } class Point3D extends Point { /* A 3D point class */ } interface PointMaker { public Point make(); } class P1 implements PointMaker { public Point make() { return new Point(); } } class P2 implements PointMaker { public Point3D make() { // This is an error! return new Point3D(); } }The rules: S is a subtype of T if: