Flashcards for topic Methods
What is the proper way to document thread-safety and serializability in APIs?
Example thread-safety documentation:
/** * This class is thread-safe. All public methods use internal synchronization * to ensure consistent state when accessed concurrently. */ public class ThreadSafeCounter { // Implementation... }
Describe the complete strategy for creating a truly immutable class that contains mutable components (like Date objects).
Creating a truly immutable class with mutable components requires:
Make the class final to prevent subclassing
Make all fields private and final
Don't provide any methods that modify state
Defensively copy all mutable components in the constructor:
// Constructor with defensive copying public ImmutablePeriod(Date start, Date end) { // Make defensive copies BEFORE validation this.start = new Date(start.getTime()); this.end = new Date(end.getTime()); // Validate using the copies if (this.start.compareTo(this.end) > 0) throw new IllegalArgumentException("Start after end"); }
Defensively copy mutable components in accessors:
// Accessor with defensive copying public Date getStart() { return new Date(start.getTime()); }
Override equals(), hashCode(), and toString() appropriately
Best practice: Use immutable components where possible (e.g., Instant instead of Date) to avoid defensive copying entirely.
When is documentation with Javadoc comments not sufficient for an API, and what additional steps should be taken?
Documentation comments alone are not sufficient for complex APIs consisting of multiple interrelated classes. In these cases:
This provides necessary context that can't be captured in individual element documentation.
How do Java 7's Objects.requireNonNull()
and Java 9's range-checking facilities in java.util.Objects
improve parameter validation, and what are their limitations?
Objects.requireNonNull() (Java 7+):
// Inline usage with assignment this.strategy = Objects.requireNonNull(strategy, "Strategy cannot be null"); // Standalone usage Objects.requireNonNull(input, "Input required");
Range-checking facilities (Java 9+):
checkFromIndexSize()
, checkFromToIndex()
, checkIndex()
// Validates index is within [0, array.length) int index = Objects.checkIndex(userIndex, array.length); // Validates range [fromIndex, toIndex) is within [0, array.length) Objects.checkFromToIndex(fromIndex, toIndex, array.length);
Limitations:
Best practice: Use these utilities for standard validations, but implement custom validation logic when needed for complex requirements.
What are the three techniques for shortening overly long parameter lists in method signatures, and what are their relative advantages?
Technique 1: Break into multiple methods
Technique 2: Create helper classes
rank
and suit
parameters with a Card
classTechnique 3: Builder pattern for method invocation
Example of increased orthogonality (Technique 1):
// Instead of this (3 parameters): int firstIndex = list.findFirstInSubList(element, fromIndex, toIndex); // Use existing methods together: List<E> subList = list.subList(fromIndex, toIndex); int firstIndex = subList.indexOf(element);
What API design advice is given for method naming and method count in an interface or class?
Method Naming Guidelines:
Method Count Principle: "Every method should pull its weight"
Guidelines for Adding Methods:
writeBoolean()
, writeInt()
, writeLong()
instead of overloaded write()
ObjectOutputStream Example: Has different method names for each type instead of overloading:
void writeBoolean(boolean v); void writeInt(int v); void writeLong(long v); // Instead of overloaded write(boolean), write(int), write(long)
What is the proper way to handle the situation where you need to add a more general method to an existing class that already has a type-specific method (for example, adding a contentEquals(CharSequence)
method to a class that already has contentEquals(StringBuffer)
)?
When adding a more general method alongside an existing specific method:
// Existing specific method public boolean contentEquals(StringBuffer sb) { // Forward to the more general implementation return contentEquals((CharSequence) sb); } // New general method public boolean contentEquals(CharSequence cs) { // Implementation that works for all CharSequences // ... }
This approach:
When implementing a method that returns an Optional containing a primitive type, what pitfall should you avoid and what alternatives exist?
Pitfall: Never return Optional<Integer>
, Optional<Double>
or Optional<Long>
as this causes double boxing (primitive → boxed → Optional), creating significant performance overhead.
Alternatives:
OptionalInt
for int valuesOptionalLong
for long valuesOptionalDouble
for double values// Inefficient - causes double boxing public Optional<Integer> findValue() { ... } // Efficient - uses specialized container public OptionalInt findValue() { // When value exists return OptionalInt.of(123); // When no value return OptionalInt.empty(); }
Note: The "minor" primitive types (Boolean, Byte, Character, Short, Float) don't have specialized Optional classes, so boxing those in a regular Optional is acceptable.
What is the best practice for handling an Optional result when you're certain that a value must be present, and what are the consequences of being wrong?
When you're certain a value exists, you can use:
// Direct access when you're certain the value exists Element element = optionalElement.get();
Consequences if wrong:
NoSuchElementException
if the Optional is emptyBetter alternatives:
assert optionalElement.isPresent() : "Element must be present"; Element element = optionalElement.get();
Element element = optionalElement.orElseThrow(() -> new IllegalStateException("Element must be present but was not"));
optionalElement.ifPresent(element -> processElement(element));
Best practice: Only use get()
when you have a provable guarantee the Optional contains a value.
How should you properly document the preconditions and postconditions of a method in Javadoc, and what is the relationship between @throws tags and preconditions?
Documenting Preconditions and Postconditions:
Preconditions: Conditions that must be true before a method is called
Postconditions: Conditions guaranteed to be true after method execution
Example:
/** * Returns a substring of this string, starting at the specified index * and extending to the end of the string. * * @param beginIndex the beginning index, inclusive; must be non-negative * and less than or equal to the length of this string * @return the specified substring * @throws IndexOutOfBoundsException if {@code beginIndex} is negative or * greater than the length of this string */ public String substring(int beginIndex) { ... }
Relationship between @throws and preconditions:
Best practices:
Showing 10 of 56 cards. Add this deck to your collection to see all cards.