Flashcards for topic Methods Common to All Objects
How should you properly compare float and double fields in an equals() method and why is special handling required?
Float and double fields require special handling due to special values like NaN, -0.0f, +0.0f, etc.
Correct way to compare:
Float.compare(float, float)
methodDouble.compare(double, double)
methodExample:
@Override public boolean equals(Object o) { // other code... // DON'T do this: // return this.doubleField == otherObj.doubleField; // DO this instead: return Double.compare(this.doubleField, otherObj.doubleField) == 0; }
Note: While you could use Float.equals() or Double.equals(), this causes autoboxing on every comparison, hurting performance.
Write a proper equals method for a Point class that allows correct behavior with subclasses and explain why this approach works.
public class Point { private final int x; private final int y; public Point(int x, int y) { this.x = x; this.y = y; } @Override public boolean equals(Object o) { // Use instanceof not getClass to allow proper subclass behavior if (!(o instanceof Point)) return false; // Cast to Point (works with all subclasses too) Point p = (Point) o; // Compare only the relevant fields for Point return p.x == x && p.y == y; } // Don't forget to override hashCode() as well }
This implementation works because:
What is a "value class" in Java and when should you override equals for such classes?
A value class:
You should override equals for a value class when:
Examples of value classes that need equals overridden:
Exception: Value classes that use instance control (ensuring at most one instance exists per value, like Enum types) don't need to override equals because object identity already provides logical equality.
Proper equals implementation enables users to compare objects as expected and allows collections to work correctly with your class.
Explain the problem of "mixed-type comparison" in equals methods and why attempting to interoperate with other types often fails.
Mixed-type comparisons in equals methods typically fail because:
Symmetry violations: If class A knows about class B but not vice versa:
// CaseInsensitiveString attempting to interoperate with String cis.equals(string) // returns true string.equals(cis) // returns false (String doesn't know about CaseInsensitiveString)
This breaks the symmetry requirement: x.equals(y) must return the same as y.equals(x)
Transitivity violations: When mixed comparisons ignore components:
colorPoint1.equals(point) // ignores color → true point.equals(colorPoint2) // point doesn't check color → true colorPoint1.equals(colorPoint2) // checks color → false if colors differ
This breaks transitivity: if a equals b and b equals c, then a must equal c
Infinite recursion risk: When two unrelated subclasses try to interoperate:
// If both implement "mixed comparison" handling: colorPoint.equals(smellPoint) // calls smellPoint.equals(colorPoint) // which
Why is the number 31 typically used as the multiplier in hashCode() implementations, and what properties make it effective?
The number 31 is commonly used in hashCode() implementations because:
It's an odd prime number (important properties):
Performance optimization:
31 * i == (i << 5) - i
Historical precedent:
This choice helps create hash functions that distribute values evenly across buckets in hash tables.
What problem occurs when significant fields are excluded from a hashCode()
implementation to improve performance?
Excluding significant fields from hashCode()
creates:
String
hash function)Example: When a pre-Java 2 String
hash function only used 16 evenly spaced characters, URLs with similar patterns would all hash to the same bucket.
Why is using Objects.hash()
for implementing hashCode()
problematic in performance-critical code?
Objects.hash()
causes performance issues because:
Example of the slower but convenient implementation:
@Override public int hashCode() { return Objects.hash(lineNum, prefix, areaCode); }
When performance is critical, manually compute the hash code using the multiplication by prime approach instead.
What are the proper steps for implementing clone()
in a class that contains mutable object references?
To properly implement clone()
with mutable object references:
super.clone()
to get a baseline shallow copyExample for a Stack class:
@Override public Stack clone() { try { Stack result = (Stack) super.clone(); // Deep copy of mutable array field result.elements = elements.clone(); return result; } catch (CloneNotSupportedException e) { throw new AssertionError(); // Can't happen } }
Note: If the mutable objects themselves contain references to other mutable objects, those would need to be cloned as well in a recursive manner.
What are the alternative approaches to object copying that are preferable to implementing the Cloneable interface, and what specific advantages do they offer?
Better alternatives to Cloneable:
Copy Constructor:
public Yum(Yum original) { this.field1 = original.field1; this.field2 = new ArrayList<>(original.field2); // Deep copy if needed // Copy remaining fields }
Static Copy Factory:
public static Yum newInstance(Yum original) { Yum copy = new Yum(); copy.field1 = original.field1; copy.field2 = new ArrayList<>(original.field2); return copy; }
Advantages over Cloneable/clone:
Exception: Arrays are best copied with clone() method.
What should a class designed for inheritance do regarding the Cloneable interface, and what are the two implementation strategies to handle this situation?
For a class designed for inheritance, do not implement Cloneable. Instead, choose one of these strategies:
Strategy 1: Allow subclasses to choose whether to implement Cloneable
// Mimics Object's behavior for subclasses @Override protected Object clone() throws CloneNotSupportedException { if (!(this instanceof Cloneable)) { throw new CloneNotSupportedException(); } // Perform proper cloning if subclass implements Cloneable return super.clone(); }
This gives subclasses the freedom to implement Cloneable or not, just as if they extended Object directly.
Strategy 2: Prevent subclasses from implementing Cloneable
// Prevents any subclass from implementing functional cloning @Override protected final Object clone() throws CloneNotSupportedException { throw new CloneNotSupportedException(); }
This completely blocks the Cloneable mechanism for all subclasses.
Either approach is better than implementing Cloneable in a superclass and forcing that design choice on all subclasses.
Showing 10 of 61 cards. Add this deck to your collection to see all cards.