-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAbstractClassExample.java
More file actions
158 lines (142 loc) · 5.58 KB
/
Copy pathAbstractClassExample.java
File metadata and controls
158 lines (142 loc) · 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package Phase3_ObjectOrientation.Abstraction;
/**
* Abstraction - Part 1: ABSTRACT CLASSES
* --------------------------------------
* ABSTRACTION is the OOP pillar that hides implementation details and exposes
* only the behaviour a caller needs. In Java you achieve abstraction with two
* mechanisms:
* <p>
*
* 1. abstract classes (this file)
* 2. interfaces (see InterfaceExample.java)
* <p>
*
* What is an abstract class?
* --------------------------
* A class declared with the `abstract` keyword. It MAY contain:
* - concrete fields and methods (with bodies)
* - abstract methods (no body, must end with a semicolon)
* - constructors (called by subclass constructors via super(...))
* <p>
*
* Properties:
* - You CANNOT instantiate an abstract class directly:
* new Shape(); // ERROR
* - A subclass either provides bodies for ALL inherited abstract methods,
* or it must ALSO be declared abstract.
* - An abstract class is essentially a "partial class" - you finish it.
* <p>
*
* When to use an abstract class
* -----------------------------
* - You have a TEMPLATE with some shared behaviour AND some pieces left
* for the subclass to fill in (Template Method pattern).
* - You want to share STATE (fields) across all subclasses - interfaces
* can have constants but not regular fields.
* - You want non-public constructors (to control how subclasses construct).
* <p>
*
* Template Method Pattern
* -----------------------
* The classic use case. A concrete method in the abstract class defines the
* overall ALGORITHM and calls abstract "hooks" that subclasses override:
* <p>
*
* abstract class Game {
* public final void play() { // template method - final
* start();
* while (!isOver()) takeTurn();
* finish();
* }
* protected abstract void start();
* protected abstract void takeTurn();
* protected abstract boolean isOver();
* protected abstract void finish();
* }
* <p>
*
* The Example Below
* -----------------
* Shape is an abstract class with:
* - a shared field `name`
* - a concrete `describe()` that uses the abstract methods
* - two abstract methods area() and perimeter()
* Circle and Rectangle complete the contract.
*/
public class AbstractClassExample {
/** Abstract base - cannot be instantiated directly. */
static abstract class Shape {
private final String name;
protected Shape(String name) {
this.name = name;
}
// Concrete - shared by all subclasses
public final String getName() {
return name;
}
// Abstract - each subclass MUST provide an implementation
public abstract double area();
public abstract double perimeter();
// Template method - uses abstract hooks to do shared work
public void describe() {
System.out.printf("%-10s area=%.2f perimeter=%.2f%n",
name, area(), perimeter());
}
}
/** Concrete subclass - completes the abstract contract. */
static class Circle extends Shape {
private final double r;
public Circle(double r) { super("Circle"); this.r = r; }
@Override public double area() { return Math.PI * r * r; }
@Override public double perimeter() { return 2 * Math.PI * r; }
}
/** Concrete subclass - completes the abstract contract. */
static class Rectangle extends Shape {
private final double w, h;
public Rectangle(double w, double h) { super("Rectangle"); this.w = w; this.h = h; }
@Override public double area() { return w * h; }
@Override public double perimeter() { return 2 * (w + h); }
}
/**
* A subclass that does NOT override all abstract methods would itself be
* abstract. Uncomment to see the compile error:
* <p>
*
* static class Triangle extends Shape {
* public Triangle() { super("Triangle"); }
* // missing area() and perimeter() -> compile error
* }
*/
public static void main(String[] args) {
// 1) Cannot instantiate the abstract base directly
// Shape impossible = new Shape("foo"); // ERROR
// 2) Use concrete subclasses through the abstract reference
Shape[] shapes = {
new Circle(2.0),
new Rectangle(3.0, 4.0),
new Circle(5.0)
};
// 3) The describe() template method calls into each subclass's
// overridden area() and perimeter() - runtime polymorphism.
for (Shape s : shapes) {
s.describe();
}
// 4) Type identity - getName() comes from the abstract class,
// area()/perimeter() come from the concrete subclass.
System.out.println("\nReflection of shapes[1]:");
System.out.println("getName() = " + shapes[1].getName());
System.out.println("getClass() = " + shapes[1].getClass().getSimpleName());
System.out.println("instanceof Shape = " + (shapes[1] instanceof Shape));
System.out.println("instanceof Rectangle= " + (shapes[1] instanceof Rectangle));
// OUTPUT
// Circle area=12.57 perimeter=12.57
// Rectangle area=12.00 perimeter=14.00
// Circle area=78.54 perimeter=31.42
//
// Reflection of shapes[1]:
// getName() = Rectangle
// getClass() = Rectangle
// instanceof Shape = true
// instanceof Rectangle= true
}
}