HAS-A is composition
Before You Start
Check each box you can do from memory. A box you cannot check yet is not a problem; it points you to a quick refresher, not a grade.
Not sure? Take the 60-second self-check.
1. Reading fields: Given the declaration below, how many instance variables does the class have?
public class Car {
private String make;
private int year;
private double price;
}
Check
Three: make, year, and price. Each field declaration is one instance variable.
2. References vs. primitives: Which of the following field declarations holds a reference to another object (rather than a primitive value)?
private int pages;
private String title;
private boolean inPrint;
Check
String title. A String is a class, so title holds a reference to a String object. int and boolean are primitive types.
3. Scope of a local variable: In the method below, does temp belong to the class?
public double totalCost() {
double temp = price * quantity;
return temp;
}
Check
No. temp is a local variable inside the method. It is created when the method is called and disappears when it returns. The class does not hold it.
If the first two questions gave you trouble, review your course notes on fields and object references before moving on.
Try This First
Here is a class. Before reading the explanation below, write down on paper or in a comment every type that Person permanently holds.
public class Person {
private String firstName;
private String lastName;
private Color color;
public Person(String fn, String ln, Color c) {
this.firstName = fn;
this.lastName = ln;
this.color = c;
}
public void setColor(Color c) {
this.color = c;
}
}
Count how many types you listed and where in the file you found them.
What You Need To Walk In With
A class HAS-A another type when and only when it declares a field of that type. The field list, not the method list, is the complete inventory of HAS-A relationships.
When you open an unfamiliar class file, the practical move is to scan from the top of the class body for every line that ends in a semicolon and sits outside any method. Those are the fields, and they are all you need to name every HAS-A relationship the class has. You can: find every field in a class you have never seen before, state each relationship in the form “X HAS-A Y”, tell apart a persistent field from a same-typed method parameter, and describe in one sentence what the relationship means in terms of how long the referenced object sticks around.
How It Works
What HAS-A means
When one class holds a reference to another object as a persistent field, that is the HAS-A relationship. The holder class owns (or at least retains) an instance of the other type for as long as the holder itself exists.
The phrase “composition” describes the structural fact: you are composing a new type by including instances of existing types as parts.
A useful analogy: a car has an engine, four wheels, and a color. The car does not become a subtype of “engine.” It simply holds one. You describe a car by listing its parts, and each part is a separate object the car keeps around.
// S20.Lab2-Enums example -- canonical three-field HAS-A class
public class Person {
private String firstName; // Person HAS-A String (first name)
private String lastName; // Person HAS-A String (last name)
private Color color; // Person HAS-A Color (enum)
// constructor, getters, setters omitted for brevity
}
Reading left to right across the field list gives you the complete HAS-A inventory: String, String, Color. Three HAS-A relationships.
The field list is the whole story
| Location in the class | Relationship |
|---|---|
Field declaration (private Color color;) |
HAS-A: the class owns a slot |
Constructor parameter (Color c) |
Temporary: used to initialize, not stored unless assigned to a field |
Method parameter (void setColor(Color c)) |
Association only: borrowed for the duration of the call |
| Local variable inside a method | No relationship to the class at all |
The setColor method above takes a Color parameter. That does not create an additional HAS-A. The HAS-A was already present via the color field. The parameter is just the mechanism for updating it.
Bigger example
// S20.Lab3 example -- six-field HAS-A class
public class Book {
private String title; // Book HAS-A String
private String isbn; // Book HAS-A String
private Genre type; // Book HAS-A Genre (enum)
private int pages; // Book HAS-A int (primitive, counted by convention)
private Author[] authors; // Book HAS-A Author array
private Publisher pub; // Book HAS-A Publisher
// ...
}
Six fields, six HAS-A relationships. int pages is a primitive; many course conventions still call this a HAS-A because the class “has” a page count as part of its state. What matters is that the relationship is declared as a field.
Quick check
Check your understanding
The class below has four field declarations and one method. How many HAS-A relationships does it have? public class Book { private String title; private String isbn; private Genre type; private int pages; private Author[] authors; public String formatCitation(String style) { // returns a formatted string } }
One class, one responsibility
Composition enforces focused class design. Book knows about books. Author knows about authors. Giving Book a private Author[] authors field allows Book to delegate author concerns to Author objects rather than duplicating author logic inside Book.
Worked Example: Predict, Then Check
Predict first. Given the class below, list every HAS-A relationship before expanding the answer.
public class EmployeeRecord {
private String employeeId;
private Person person;
private Department dept;
private double salary;
public EmployeeRecord(String id, Person p, Department d, double sal) {
this.employeeId = id;
this.person = p;
this.dept = d;
this.salary = sal;
}
public void transfer(Department newDept) {
this.dept = newDept;
}
}
Write your list, then expand.
Algorithmic steps to find HAS-A relationships
- Find the opening
{of the class body. - Read down until you find the first
}that closes the class (not a method or nested block). - Collect every line that declares a field:
<modifier> <type> <name>;. - For each field, record: “EmployeeRecord HAS-A
<type>”. - Stop. Methods, constructors, and local variables are outside the scope of this search.
Show answer
| Field | HAS-A statement |
|---|---|
private String employeeId |
EmployeeRecord HAS-A String |
private Person person |
EmployeeRecord HAS-A Person |
private Department dept |
EmployeeRecord HAS-A Department |
private double salary |
EmployeeRecord HAS-A double (primitive) |
The transfer(Department newDept) method takes a Department as a parameter. That is not a fifth HAS-A. It is a temporary association: newDept exists only for the duration of the call, after which its value is assigned to the dept field (the existing HAS-A).
Common Misconceptions
a method parameter of type T means the class HAS-A T.
Wrong mental model: “Person has a method setColor(Color c), so Person HAS-A Color because of that method.”
Why it breaks: Consider a Printer class with a method print(Document d). A printer temporarily receives a document to print, but it does not permanently hold a document. If you listed every method-parameter type as a HAS-A, every class that accepted a String in any method would “have” a String -- which would mean every class HAS-A everything.
Correction: Look at field declarations only. setColor(Color c) exists to update the color field -- it is the field that establishes the HAS-A. The method is incidental.
Source: BJP (Reges and Stepp); Rumbaugh et al., The Unified Modeling Language Reference Manual, 2nd ed. (Addison-Wesley, 2004), pp. 38-42 (association vs. composition).
Quick check
Check your understanding
A class named Printer has a method: public void print(Document d) { ... } but no field of type Document. Which statement is correct?
if two classes share fields, the second should extend the first.
Wrong mental model: “EmployeeRecord has all the fields Person has, plus more, so EmployeeRecord extends Person.”
Why it breaks: extends declares IS-A, which means substitutability. Every method that accepts a Person must also accept an EmployeeRecord. An employee record is not a person -- it describes one. Accepting an EmployeeRecord in place of a Person wherever a person is expected would expose employee-specific behavior in contexts that should not know about it.
Correction: Use HAS-A: class EmployeeRecord { private Person person; ... }. The employee record has a person, giving access to all person behavior through delegation, without false substitutability.
Source: Bloch, Effective Java, 3rd ed. (Addison-Wesley, 2018), Item 18, “Favor composition over inheritance”; Liskov and Wing, “A behavioral notion of subtyping,” ACM Transactions on Programming Languages and Systems 16.6 (1994), pp. 1811-1841.
Formal Definition and Interface Contract
The Java Language Specification (Java SE 25, JLS §8.3) defines a field declaration as the mechanism by which a class declares a variable that is part of the state of each instance of the class. Each such variable persists for the lifetime of the object that contains it.
UML 2.5 defines composition as a strong form of association in which the part’s lifetime is governed by the whole (OMG UML Specification, v2.5.1, §11.5.4). In Java, a private field of a reference type is the standard realization of this relationship: the containing object holds a reference to the part object, and if the containing object is collected, the reference is gone.
Bloch’s Effective Java, 3rd ed., Item 18, provides the authoritative practical guidance: “Favor composition over inheritance” when the relationship fails the IS-A test (substitutability). Composition gives you the benefits of code reuse without the coupling and fragility of inheritance.
Mental Model
Picture a class as a labeled box with named slots inside it. Each slot holds a value or a reference to another object. Reading the label on each slot tells you what type lives there. Those labels are the HAS-A relationships. The box’s methods are the behavior the box offers to the outside world -- they use the slots but are not part of the ownership list. When you want to know what a class is composed of, read the slot labels, not the instruction manual.
Connections
Within CSCD 211: The HAS-A concept appears in every lab that defines a multi-class design; knowing it cold makes every subsequent design discussion faster.
Looking back: CSCD 210 introduced fields and instance variables. HAS-A is the relationship name for what you already knew how to write. BJP (Reges and Stepp) covers both the syntax and this vocabulary simultaneously.
Looking ahead: The next concept distinguishes the three flavors of HAS-A (association, aggregation, and composition). After that, IS-A (inheritance) is introduced, and the contrast between the two becomes the central design question in every multi-class program.
Practice
Level 1
Read the class below and list every HAS-A relationship.
public class Address {
private String street;
private String city;
private String state;
private int zipCode;
}
Thought process
Count the fields. Each field is one HAS-A. Ignore anything that is not a field declaration.
Show answer
| Field | HAS-A statement |
|---|---|
private String street |
Address HAS-A String |
private String city |
Address HAS-A String |
private String state |
Address HAS-A String |
private int zipCode |
Address HAS-A int |
Four HAS-A relationships. All four are declared as fields in the class body.
Level 2
The class below has four field declarations and two methods. Identify which HAS-A relationships exist, and explain why one of the method parameters does not add a new HAS-A.
public class Order {
private String orderId;
private Customer customer;
private Product product;
private int quantity;
public void applyDiscount(Coupon coupon) {
// applies coupon logic -- not shown
}
public double totalPrice(double taxRate) {
return product.getPrice() * quantity * (1 + taxRate);
}
}
Thought process
- Scan the class body for field declarations (lines that are not inside any method or constructor).
- List those fields as HAS-A.
- Note what types appear in method signatures but not in the field list.
- Explain the difference in terms of scope and ownership.
Show answer
HAS-A relationships (from the field list):
| Field | HAS-A statement |
|---|---|
private String orderId |
Order HAS-A String |
private Customer customer |
Order HAS-A Customer |
private Product product |
Order HAS-A Product |
private int quantity |
Order HAS-A int |
Coupon appears only as a parameter to applyDiscount. The Order class does not hold a Coupon field; it borrows a Coupon for the duration of that call. There is no HAS-A relationship with Coupon. Similarly, double taxRate in totalPrice is a local computation input, not stored state.
Level 3
A teammate wrote this design. They want EmployeeRecord to extend Person so that it “gets all the Person fields for free.” Evaluate the design. Propose a HAS-A alternative and explain what problem it avoids.
// Proposed design
public class EmployeeRecord extends Person {
private String employeeId;
private double salary;
}
Thought process
Apply the substitutability test: can every operation that works on a Person work equally well on an EmployeeRecord? Think about methods that take a Person parameter. What happens if they receive an EmployeeRecord instead? Is that always safe? Then sketch the HAS-A alternative and compare field access between the two designs.
Show answer
The substitutability test fails in the general case. extends Person means that wherever a Person is expected (as a method parameter, in a collection of Person objects, etc.) an EmployeeRecord can silently appear. An EmployeeRecord exposes employee-specific behavior (salary, employee ID) in contexts that should only see Person behavior. That is a violation of the Liskov Substitution Principle and will cause fragility as the code grows.
The HAS-A alternative:
public class EmployeeRecord {
private String employeeId; // EmployeeRecord HAS-A String
private Person person; // EmployeeRecord HAS-A Person
private double salary; // EmployeeRecord HAS-A double
}
With this design, EmployeeRecord is not substitutable for Person -- which correctly reflects the real-world relationship. Access to person data goes through record.getPerson().getFirstName(), which is slightly more verbose but makes the boundary explicit. This is the pattern Bloch recommends in Effective Java, 3rd ed., Item 18: favor composition over inheritance when the IS-A test does not hold.
Go Deeper (optional)
None of the depth here is needed to write correct code. If you are comfortable with the field-list rule and want to keep moving, skip this and go straight to Check Yourself.
Where the design principle comes from. The idea that each class should have one focused responsibility is not a Java invention. David Parnas formalized it in 1972 (“On the Criteria To Be Used in Decomposing Systems into Modules,” Communications of the ACM 15.12, pp. 1053-1058), arguing that modules should hide a single design decision. Composition is the structural tool that makes that possible: instead of one large class knowing about everything, you break the knowledge into focused types and connect them with HAS-A fields.
The type-theory connection. When a class is composed of a fixed set of named fields, it corresponds to what type theorists call a product type (the class holds a value of type A AND a value of type B AND a value of type C). This contrasts with inheritance, which is closer to a sum type (a value is either an A or a B). Recognizing this mapping helps explain why HAS-A and IS-A behave so differently in design: product types are extensible by adding new fields; sum types are extensible by adding new cases.
The design-pattern connection. The idea of passing a separate object (such as a Comparator) that carries one piece of behavior into a class that needs it is, at its core, the Strategy pattern from Gamma et al., Design Patterns (Addison-Wesley, 1994). A Comparator field is also the simplest example of a first-class function in Java: you are storing behavior as an object. HAS-A is what makes that possible.
The UML picture. In UML class diagrams, composition is drawn as an association line with a filled diamond at the owning end. Aggregation (a weaker form of HAS-A, where the part can outlive the whole) uses an open diamond. Java field declarations cover both; the distinction between filled and open diamond is a design-intent call that matters when you read architecture diagrams on the job.
In professional code. The ability to open an unfamiliar file and immediately describe its structure in plain English (“this class holds a String, an enum, and an array of Author objects”) is exactly what code reviewers and senior engineers do in the first thirty seconds of reading any class. That fluency starts here, with the field-list rule.
Check Yourself
Close the notes and answer each one from memory, then reveal it. Pulling an idea back from memory is one of the strongest ways to make it stick.
Check your understanding
Which location in a class declaration establishes a HAS-A relationship?
How many HAS-A relationships does the following class have? public class Address { private String street; private String city; private String state; private int zipCode; }
A class has the method signature: public void applyDiscount(Coupon coupon) but no field of type Coupon. What is the relationship between this class and Coupon?
Predict the HAS-A relationships for this class: public class Order { private String orderId; private Customer customer; private Product product; private int quantity; public void applyDiscount(Coupon coupon) { } public double totalPrice(double taxRate) { return product.getPrice() quantity (1 + taxRate); } } Which of the following correctly lists all and only the HAS-A relationships?
A teammate proposes: public class EmployeeRecord extends Person { ... } because EmployeeRecord needs the same fields as Person. What is the primary problem with this design according to the substitutability principle?