AmouAI Hub/Courses/Programming Fundamentals/Day 18
Java Classes, Objects & Encapsulation
Constructors, access modifiers, and why Java needs getters and setters where Python reaches for @property.
By the end of today you can
- Write a Java class with fields, a constructor and methods
- Overload a constructor, and chain with
this(...) - Choose between
public,privateandprotecteddeliberately - Say why Java's
privateis enforced and Python's_nameis not - Write getters and setters, and say what they buy
- Override
toString,equalsandhashCode— and why the last two travel together - Translate a Python class into Java without losing its invariants
▶Today's videos
Watch each video, then work the matching sections below. Watching alone will not do it.
this versus self · constructor overloading and chaining with this(...) · final · the integer division bug that Java's type system does not save you from.public/private/protected enforced by a compiler · getters and setters, and why @property made them unnecessary in Python · the equals/hashCode contract and the silent bug when you break it · records.1The same class, in both languages
Start with Day 12's Book and see what Java demands.
read / pages where both are int gives an int — 100 / 292 is 0, and progress() cheerfully returns 0.0. No error, no warning. This is a semantic error in exactly the sense Day 5 defined, and Java's type system does not save you from it. Cast one side to double.
final
private final String title says the field can never be reassigned after construction. Python has no equivalent — the closest is a @property with no setter. Marking fields final wherever possible removes a whole category of "who changed this?" questions.
2Constructors
Overloaded, and chained.
public class Book {
private final String title;
private final int pages;
private int read;
// full constructor
public Book(String title, int pages, int read) {
if (pages <= 0) {
throw new IllegalArgumentException("pages must be positive: " + pages);
}
this.title = title;
this.pages = pages;
this.read = read;
}
// convenience constructor — delegates, never duplicates
public Book(String title, int pages) {
this(title, pages, 0);
}
}this(...) must be the first statementChaining to another constructor is how you avoid copying the validation into every overload. Java enforces that the chained call comes first, so the object is never half-built. Python's equivalent is a default argument — one constructor, read=0 — which is simpler, and is why Python needs no chaining rule.
3Access modifiers
Where Python asks nicely, Java refuses.
| Modifier | Visible to | Python's nearest thing |
|---|---|---|
public | Everything | a plain name |
protected | This class, subclasses, same package | _name, loosely |
| (none) | Same package only | no equivalent |
private | This class only | __name, and even that is only mangled |
Which is why Java needs getters
public class Book {
private int read = 0;
private final int pages;
public int getRead() {
return read;
}
public void setRead(int value) {
if (value < 0 || value > pages) {
throw new IllegalArgumentException("read out of range: " + value);
}
this.read = value;
}
}Java writes getRead()/setRead() from the start, because turning a public field into a method later would break every caller. Python does not need to: @property lets book.read keep working while validation appears behind it. Same goal — controlled access — reached from opposite directions.
Which is better? Java's is more ceremony and more guarantee. Python's is less ceremony and relies on the team. You have now written both, so the opinion is yours.
4toString, equals and hashCode
Three methods every value class should override — and two of them are a pair.
import java.util.Objects;
public class Contact {
private final String name;
private final String email;
public Contact(String name, String email) {
this.name = name;
this.email = email;
}
@Override
public String toString() {
return "Contact(" + name + ", " + email + ")";
}
@Override
public boolean equals(Object other) {
if (this == other) return true;
if (!(other instanceof Contact)) return false;
Contact c = (Contact) other;
return name.equals(c.name) && email.equals(c.email);
}
@Override
public int hashCode() {
return Objects.hash(name, email);
}
}The contract is: equal objects must have equal hash codes. Break it and your objects behave correctly with .equals() but wrongly in a HashMap or HashSet — you put a contact in, ask whether an equal one is present, and get false. This is Day 9's hashing lesson: the hash decides the bucket, and if two equal objects hash differently they land in different buckets and never meet.
Since Java 16: public record Contact(String name, String email) {} generates the constructor, getters, toString, equals and hashCode for you — the whole file above in one line. Write it by hand once so you know what is being generated, then use records. Python's equivalent is @dataclass.
>_Python playground
A real Python interpreter running inside your browser. Nothing is installed, nothing is uploaded, nothing can break.
input(), comma separated →
✓Exercise set
Checked automatically the moment you submit. Work top to bottom — each one assumes the last. Your answers are saved in this browser.
Day 19 — Java Inheritance, Interfaces & Polymorphism
Interfaces are the idea Python never forces you to learn.