Connect cohesive class design and composition with Java's interface, method-overloading, return-type, and default-constructor rules.
Answer: Related responsibilities are easier to understand, test, and change together. A focused class often exposes a smaller, clearer contract and can be reused without bringing unrelated behavior along with it.
These are potential benefits, not a guarantee of fewer bugs or better performance. Evaluate the actual responsibilities and dependencies. Excessively fragmenting a coherent operation across many tiny classes can make the system harder to follow.
Answer: It groups closely related data and behavior while separating unrelated responsibilities. A class that calculates an invoice total need not also persist the invoice and send delivery notifications.
The separated parts are often collaborating classes, not subclasses of one another. Inheritance should express a valid subtype relationship. Breaking a large class into a hierarchy merely to make it smaller does not automatically improve cohesion.
Answer: Ask whether a component's operations and state contribute to a connected purpose and whether they tend to change for related reasons. Cohesion can be discussed for a class, package, or larger component.
For example, tax and discount calculations may belong together in a pricing component, while SMTP connection management probably belongs elsewhere. The right boundary depends on the domain; method count alone does not establish cohesion.
Answer: Commonly, an object stores a reference to another object in a field and delegates work to it. For example, a Car can have an Engine without being a subtype of Engine.
A reference field by itself does not specify ownership or lifetime. The referenced object might be shared, externally supplied, or exclusively owned. Document those choices, especially when the collaborator is mutable or owns a resource that must be closed.
Answer: It allows an overriding method to return a more specific reference type than the overridden declaration. For example, a copy method returning Document can be overridden by a method returning Invoice when Invoice is a Document subtype.
Primitive return types do not have this covariance. The compile-time type of the selected method still determines the result type seen by the caller, so a call through a Document expression does not automatically acquire an Invoice result type.
Answer: Same-named methods must have distinct parameter signatures under Java's rules. Differences in parameter number, types, or order can establish an overload.
Changing parameter names, return type, access, or throws alone cannot. Erasure also matters: methods taking List<String> and List<Integer> cannot be distinguished solely by those erased type arguments.
Answer: Once the parameter signatures are validly distinct, overloads can have different return types, access modifiers, and throws clauses. Each declaration must still obey its own language constraints and any independent inheritance obligations.
Callers are checked against the overload actually selected. An inaccessible overload is not a general alternative a caller can freely choose, and exceptions from one overload do not automatically become the contract of the others. Keep the overload family conceptually consistent.
Answer: A class can implement multiple interfaces, and an interface can extend multiple interfaces. A concrete class must satisfy its remaining abstract-method obligations using valid declared or inherited implementations.
Interfaces can provide default behavior, static utilities, and private helpers, but do not give implementing objects additional interface instance fields. Conflicting inherited defaults and incompatible method contracts must be resolved according to the language rules.
Answer: Inheritance makes one type a subtype of another and can inherit behavior. Composition builds behavior by holding and using collaborators. It is often appropriate when a component needs a service without being a specialized version of that service.
import java.util.Objects;
public class ComposedReport {
interface Formatter {
String format(String title);
}
static final class Report {
private final Formatter formatter;
Report(Formatter formatter) {
this.formatter = Objects.requireNonNull(formatter);
}
String render(String title) { return formatter.format(title); }
}
public static void main(String[] args) {
Report report = new Report(title -> "Report: " + title);
System.out.println(report.render("Sales"));
}
}
The program prints Report: Sales. Report has a Formatter and delegates formatting; it does not extend a formatter class. The caller supplies the collaborator, making the dependency explicit and replaceable. Use inheritance when substitutability is intended and the supertype contract remains valid.
Answer: For an ordinary class with no constructor declarations, it is the constructor implicitly declared by the compiler. In the usual top-level or static nested class case, it has no parameters, takes the class's access level, and invokes the superclass's no-argument constructor.
That superclass invocation must be legal; the compiler does not create a missing parent overload. Object has no superclass, inner classes involve an enclosing instance, and specialized class forms have additional rules. A no-argument constructor written by the programmer is not the compiler-provided default constructor.
References: JLS: classes, methods, and default constructors and JLS: interfaces. Cohesion and composition guidance describes design choices rather than extra compiler rules.