Composition, enum and passing references to methods
An object of another class as a field of a class (has-a), enum for fixed sets of values, and the consequences of object references going to a method as a copy: a method can change the object but cannot turn the caller's variable into another object.
This week has three topics, and all three are about how objects relate to each other. Composition means having other objects inside an object (an employee’s birth date, a car’s engine). enum safely represents a fixed and limited set of values (days, levels, planets). Finally, you will learn exactly what changes and what does not change when you pass objects to a method. This third topic is the sneakiest source of “what is printed?” questions on the final exam.
Golden rules:
- Composition = has-a. A field of a class can be an object of another class.
- enum constants are not created with new, they are used as
Day.WEDand compared with==. - Java is always pass-by-value. For objects the thing copied is the reference.
- A method can change the inside of an object; it cannot turn the caller’s variable into another object.
==checks whether it is the same object,equalschecks the contents (if overridden).
Composition: the has-a relationship
class Date {
private int day, month, year;
public Date(int day, int month, int year) {
this.day = day; this.month = month; this.year = year;
}
public int getYear() { return year; }
public String toString() { return day + "/" + month + "/" + year; }
...
}
class Employee {
private String name;
private Date birthDate; // an object of another class
public Employee(String name, Date birthDate) {
this.name = name;
this.birthDate = birthDate;
}
public Date getBirthDate() { return birthDate; }
public String toString() { return name + ", born " + birthDate; }
}
// in main:
Date birth = new Date(14, 3, 2005);
Employee e = new Employee("Deniz", birth);
System.out.println(e);
System.out.println(e.getBirthDate().getYear());
Deniz, born 14/3/2005
2005
- When Employee’s toString concatenated
birthDatewith a String, Date’s toString was called automatically. e.getBirthDate().getYear()is a chain: first get the Date object, then call its method.- If the sentence “an employee has a date” sounds right, composition is the right choice.
The second example, handing the job to the inner object (delegation):
class Engine {
private int cc;
public Engine(int cc) { this.cc = cc; }
public int getCc() { return cc; }
public void setCc(int cc) { this.cc = cc; }
public void run() { System.out.println("Engine " + cc + "cc running"); }
}
// Car: private String model; private Engine engine; start() { print(model + ": "); engine.run(); }
Car car = new Car("Egea", new Engine(1400));
car.start();
System.out.println(car.getEngine().getCc());
Egea: Engine 1400cc running
1400
PITFALL · a getter hands out the inner object.
getBirthDate()returns not a copy of the Date but its reference. Also, the Date object given to the constructor was not copied either: the same reference was stored. Whendd.setYear(1990)was done outside, the employee’s birth year also became 1990 (in verificatione2.getBirthDate().getYear()→ 1990). Even if the field is private, if you share a changeable inner object you cannot protect its inside.
enum
Some variables can take only one of a few values: the day of the week, a traffic light, a difficulty level. Keeping these with an int (0 = Monday) or a String ("Mon") invites mistakes: nothing stops anyone from writing day = 9 or "mon". enum defines this set as a type:
enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }
Day d = Day.WED;
System.out.println(d + " " + d.ordinal() + " " + d.name());
System.out.println(d == Day.WED);
for (Day x : Day.values()) System.out.print(x + " ");
System.out.println();
System.out.println(Day.values().length);
System.out.println(Day.valueOf("FRI").ordinal());
WED 2 WED
true
MON TUE WED THU FRI SAT SUN
7
4
| Expression | What it gives |
|---|---|
Day.WED | the constant itself |
d.ordinal() | declaration position, from 0 |
d.name(), println(d) | its name: "WED" |
Day.values() | all constants, in order, as an array |
Day.valueOf("FRI") | the constant from a String; IllegalArgumentException if absent |
d == Day.WED | comparison (== is safe with enums) |
new Day() cannot be written: enum classes may not be instantiated. The constants are created once at the start of the program, that is all.
switch with an enum
static String advice(Level lv) {
switch (lv) {
case LOW: return "Devam"; // just LOW, not Level.LOW
case MEDIUM: return "Dikkat";
case HIGH: return "Dur";
default: return "?";
}
}
// main: advice(Level.LOW) + " " + advice(Level.HIGH)
Devam Dur
An enum with fields and a constructor
An enum is actually a special class; it can have fields, a constructor and methods. Each constant calls the constructor with its own argument:
enum Coin {
QUARTER(25), HALF(50), LIRA(100); // ; after the constants
private final int kurus;
Coin(int k) { kurus = k; } // the constructor is not written public
public int getKurus() { return kurus; }
}
// in main:
for (Coin c : Coin.values()) System.out.print(c + "=" + c.getKurus() + " ");
System.out.println();
int total = Coin.HALF.getKurus() * 2 + Coin.QUARTER.getKurus();
System.out.println(total);
QUARTER=25 HALF=50 LIRA=100
125
Passing references to methods
In Java everything is pass-by-value: a copy of a value goes to the method. For a primitive, the thing copied is the number itself. For an object, the thing copied is the reference (the address of the object). This single rule has three consequences:
static void rename(Pet p) { p.setName("Boncuk"); } // 1) change the inside
static void replace(Pet p) { p = new Pet("Yeni"); p.setName("Degisti"); } // 2) turn it into a new object
static void swap(Pet a, Pet b) { Pet t = a; a = b; b = t; } // 3) swap
// in main:
Pet pet = new Pet("Pamuk");
rename(pet);
System.out.println(pet.getName());
replace(pet);
System.out.println(pet.getName());
Pet a = new Pet("A"), b = new Pet("B");
swap(a, b);
System.out.println(a.getName() + " " + b.getName());
Boncuk
Boncuk
A B
rename call:
main.pet ──┐
├──► [Pet: "Pamuk" → "Boncuk"] the same object, its inside changed
rename.p ──┘
replace call:
main.pet ─────► [Pet: "Boncuk"] untouched
replace.p ────► [Pet: "Degisti"] a new object, lost when the method ends
| What is the method doing? | Does the caller see it? |
|---|---|
| Calling a method of the object through the parameter and changing its inside | Yes |
Assigning a new object to the parameter (p = new ...) | No |
| Swapping two parameters | No |
| Changing a primitive parameter | No |
| Creating a new object and returning it | Yes, if you assign the returned value |
If you want to get a new object from a method, return it: Pet m = make("Minnos");.
When you see a question of this kind in the exam, ask yourself for each method call: is the method doing a setter or an element assignment through the parameter (the caller sees it), or is it assigning to the parameter itself (the caller does not see it)? This single question decides the answer.
== and equals
Pet x1 = new Pet("Tom"), x2 = new Pet("Tom");
System.out.println((x1 == x2) + " " + x1.equals(x2));
Point p1 = new Point(1, 2), p2 = new Point(1, 2);
System.out.println((p1 == p2) + " " + p1.equals(p2));
false false
false true
Pet did not override equals; the default equals from Object does the same thing as ==. Point, on the other hand, defined equals itself and compares x and y:
@Override
public boolean equals(Object o) {
if (!(o instanceof Point)) return false;
Point other = (Point) o;
return x == other.x && y == other.y;
}
String’s equals is also a method overridden like this; that is why "abc".equals(s) looks at the contents.
Common mistakes
| Wrong idea | The truth |
|---|---|
| For composition make the field public | private field + constructor + a getter if needed |
| A getter gives a copy of the inner object | It gives its reference |
Day d = new Day(); | Day d = Day.MON; |
case Day.MON: | case MON: |
ordinal() starts from 1 | From 0 |
valueOf("mon") works | It is case-sensitive; an exception if absent |
| Java passes objects by reference | Pass-by-value; the thing copied is the reference |
When a method says p = new Pet(...), the object in main changes | It does not change |
| A swap method swaps the objects | It does not |
x1 == x2 compares contents | Same object or not; use equals for contents |
Test yourself
Exam-style questions. Solve them on paper first, then reveal the answer.
Employee has a Date birthDate field; Date(day, month, year) has getters. What is printed?
Employee q = new Employee("Ece", new Date(5, 9, 2004));
System.out.println(q.getBirthDate().getMonth() + q.getBirthDate().getDay());Show answer
getBirthDate() returns a Date object, and another method can be called on it right away (a chain). The month is 9, the day is 5; both are ints, so addition: 14.
Given enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }, what is printed?
System.out.println(Day.MON.ordinal() + Day.SUN.ordinal() + " " + Day.values()[2]);Show answer
ordinal() is the position of the constant, starting from 0: MON 0, SUN 6. 0 + 6 = 6 (int addition). values() gives the constants as an array in declaration order; index 2 is WED. An enum is printed by its name in println.
Given enum Level { LOW, MEDIUM, HIGH }, what is printed?
Level lv = Level.MEDIUM;
switch (lv) {
case LOW: System.out.print("L");
case MEDIUM: System.out.print("M");
case HIGH: System.out.print("H");
}Show answer
A switch can be written with an enum; the class name is not written in the cases (case LOW, not Level.LOW). It enters at MEDIUM and, because there is no break, falls into HIGH: MH.
rename(Pet p) calls p.setName("Boncuk"). What is printed?
Pet q = new Pet("Zeytin");
Pet q2 = q;
rename(q2);
System.out.println(q.getName());Show answer
q and q2 point to the same object. A copy of that object's reference goes to rename; the method changes the name of the same object. All three references look at a single object.
replace(Pet p) does p = new Pet("Yeni"); p.setName("Degisti"); and rename sets the name to Boncuk. What is printed?
Pet q = new Pet("Fistik");
replace(q);
rename(q);
replace(q);
System.out.println(q.getName());Show answer
Inside replace, p = new Pet(...) turns only the method's local copy to the new object; q in main is still on the old object. The object that replace changed is lost when the method ends. Only the effect of rename remains.
What is printed?
static void modify(int[] arr, int v) { arr[0] = 100; v = 100; }
// in main:
int[] nums = {1, 2, 3};
int k = 5;
modify(nums, k);
System.out.println(nums[0] + " " + k);Show answer
Both are pass-by-value. For the array, the thing copied is the reference; the method changes an element of the same array. For the int, the thing copied is the value itself; changing v does not touch k.
Car has a private Engine engine field and getEngine() returns it; Engine has getCc() and setCc(int). What is printed?
Car c = new Car("Clio", new Engine(1000));
Engine en = c.getEngine();
en.setCc(1600);
System.out.println(c.getEngine().getCc());Show answer
The getter returns not a copy of the object but its reference. en is the very engine inside the car; changing it changes the car's engine. Even if the field is private, if you hand out a changeable object, its inside is not protected.
enum Coin { QUARTER(25), HALF(50), LIRA(100); ... } has getKurus(). What is printed?
System.out.println(Coin.valueOf("QUARTER").getKurus() + Coin.values().length);Show answer
valueOf("QUARTER") finds the constant from the String; its kurus value is 25. values().length is the number of constants: 3. 25 + 3 = 28.
Point overrides equals to compare x and y. What is printed?
Point a = new Point(3, 4), b = new Point(3, 4), c = a;
System.out.println((a == b) + " " + a.equals(b) + " " + (a == c));Show answer
== checks whether it is the same object: a and b are two separate objects → false. Because equals is overridden, it compares the contents → true. c = a is the same object → true.
Given enum Level { LOW, HIGH }: (a) does Level x = new Level(); compile? (b) what happens at runtime with Level.valueOf("MID")?
Show answer
The enum constants are fixed; new ones cannot be produced with new, and the compiler says enum classes may not be instantiated. If valueOf is called with a name that does not exist, it compiles but crashes while running with No enum constant Level.MID.
Write an enum Planet with constants EARTH(9.81) and MARS(3.71), a private final double field gravity, a constructor and a getter.
Show answer
A ; is put after the constants; the fields and methods come after it. An enum constructor is not written public (it is automatically private).enum Planet { EARTH(9.81), MARS(3.71); private final double gravity; Planet(double g) { gravity = g; } public double getGravity() { return gravity; }}Planet.MARS + " " + Planet.MARS.getGravity() → MARS 3.71.
Write a class Car that has a model (String) and an Engine (composition), a constructor taking both, and a start() method that prints the model followed by the result of the engine's run().
Show answer
Car has an Engine (has-a). Car does not do the job itself, it hands it to the engine (delegation).class Car { private String model; private Engine engine; public Car(String model, Engine engine) { this.model = model; this.engine = engine; } public void start() { System.out.print(model + ": "); engine.run(); }}new Car("Egea", new Engine(1400)).start() → Egea: Engine 1400cc running.
Verification: All outputs were run with JDK 26 (verify/SEN1001/W13.java; the classes Date, Employee, Engine, Car, Pet, Point and the enums Day, Level, Planet, Coin are in the same file). The enum errors were obtained with javac and java under verify/SEN1001/err/en/.