Final and Private Modifiers
class P {
private final int x;
P(int x) {
this.x = x;
}
P foo() {
return new P(this.x + 1);
}
P bar(P p) {
p.x = p.x + 1; // this mutation cannot happen as 'final'
return p;
}
}
class Q {
P baz(P p) {
return new P(p.x + 1); // this cannot happen as x in class P is 'private'
}
}
error: cannot assign a value to final variable x
error: x has private access in P
Tell-Don’t-Ask
record DoublePair(double fst, double snd) {}
// does not violate as it's a record class that holds immutable data (has final & private modifiers). It's a shortcut for a class.
class Point {
private final DoublePair coord;
Point(double x, double y) {
this.coord = new DoublePair(x, y);
}
private double getX() { // does not violate as it's private
return this.coord.fst();
}
private double getY() {
return this.coord.snd();
}
double distanceTo(Point otherPoint) {
double dx = this.getX() - otherPoint.getX();
double dy = this.getY() - otherPoint.getY();
return Math.sqrt(dx * dx + dy * dy);
}
public String toString() {
return "(" + this.getX() + ", " + this.getY() + ")";
}
}
Method Overloading
- compile-time polymorphism
- Same method name
- Different type signature
- Return type does not matter
(a) class A1 - ✅
void f(int x) {}
void f(boolean y) {}
- Different parameter types:
intvsboolean - This is valid overloading ✅
(b) class A2 - ❌ ****
void f(int x) {}
void f(int y) {}
- Same signature: both take
int - Parameter name (
xvsy) doesn’t matter - only the type matters - Error: “method f(int) is already defined” ❌
(c) class A3 - ❌
private void f(int x) {}
void f(int y) {}
- Wait… both take
int! - Error: “method f(int) is already defined” ❌
- Access modifier (
privatevs default) doesn’t make them different methods
(d) class A4 - ❌
int f(int x) { return x; }
void f(int y) {}
- Both have signature
f(int) - Different return types (
intvsvoid) doesn’t matter - Error: “method f(int) is already defined” ❌
(e) class A5 - ✅
void f(int x, String s) {}
void f(String s, int y) {}
- Different parameter order:
(int, String)vs(String, int) - These are different signatures, so valid overloading ✅
Method Overriding
- run-time polymorphism
- A method in sub-class can override the same method in its super-class
- Same method name and same parameters’ types signature (number, type, and order)
- Covariant return type
Format
@Override
ReturnType methodName(parameters) {
// new implementation
}
Task
class FormattedText {
private final String text;
private final boolean isUnderlined;
FormattedText(String text) {
this.text = text;
this.isUnderlined = false;
}
/*
* Overloaded constructor, but made private to prevent
* clients from calling it directly.
*/
private FormattedText(String text, boolean isUnderlined) {
this.text = text;
this.isUnderlined = isUnderlined;
}
FormattedText toggleUnderline() {
return new FormattedText(this.text, !this.isUnderlined);
}
@Override
public String toString() {
return this.text + (this.isUnderlined ? "(underlined)" : "");
}
}
When we do FormattedText(“cs2030”).toggleUnderline(), it becomes underlined. For plain text, when we toggleUnderline(), how do we make sure it doesn’t actually underline.
class PlainText extends FormattedText {
PlainText(String text) {
super(text, false); // Always start NOT underlined
}
@Override
FormattedText toggleUnderline() {
// Return a new PlainText with the same text, still NOT underlined
return new PlainText(this.text);
}
}
Static vs Instance
Instance method belong to an object and can access its object’s fileds/date. Static methods belong to the class as a whole and cannot access instance-specific data
void
For methods that return nothing. More for side-effects methods.