Flashcards for topic Methods
When is it appropriate not to make defensive copies of mutable parameters in Java methods?
Defensive copying can be omitted in these specific circumstances:
When there's an explicit trust relationship:
When the method/constructor indicates an explicit "handoff" of the object referenced by a parameter
When there are significant performance concerns and the overhead of defensive copying is problematic
When using immutable parameters (preferred solution where possible)
Note: Even when omitting defensive copies, document the expectations clearly to prevent invariant violations.
What are the potential failure modes when a method doesn't properly validate its parameters, and how do they escalate in severity?
Parameter validation failures escalate in severity (from least to most severe):
Quick obvious failure: Method fails with a relevant exception at the beginning
Confusing exception: Method fails with unrelated exception during processing
Silent incorrect result: Method completes but returns wrong result
Corrupted state: Method completes but leaves object in inconsistent state
The severity increases based on:
Always validate parameters early to prevent escalation to more severe failure modes.
What problematic behavior occurs when autoboxing interacts with method overloading, particularly with the List
interface's remove
methods?
List<E>
has two overloaded remove
methods that can cause confusion:
remove(E)
: Removes an element matching the valueremove(int)
: Removes element at a specific positionWhen using primitive int
with lists of Integer
:
list.remove(i)
selects remove(int)
removing by positionlist.remove((Integer)i)
selects remove(E)
removing by valueExample of confusing behavior:
List<Integer> list = new ArrayList<>(Arrays.asList(-3, -2, -1, 0, 1, 2)); for (int i = 0; i < 3; i++) { list.remove(i); // Removes elements at positions 0, 1, 2 (not values 0, 1, 2) } // Result: [-2, 0, 2] (not [-3, -2, -1] as might be expected)
list.remove((Integer) i); // or list.remove(Integer.valueOf(i));
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 recommended safe, conservative policy for method overloading to avoid potential confusion?
Core Conservative Policy: Never export two overloadings with the same number of parameters
For Varargs Methods: Do not overload varargs methods at all (with few exceptions)
Rationale: Ensures programmers are never in doubt about which overloading applies to any set of parameters
Alternative Approach: Use different method names instead of overloading
write(int)
, write(long)
, etc.writeInt(int)
, writeLong(long)
, etc.Special Case for Constructors:
Lambda Method Reference Issues:
// This works: new Thread(System.out::println).start(); // This doesn't compile: exec.submit(System.out::println); // Ambiguous between Runnable and Callable
When using varargs in performance-critical code, what optimization technique can be applied when most calls use a small number of arguments?
For performance-critical varargs methods where most calls use few arguments:
public void foo() { /* implementation */ } public void foo(int a1) { /* implementation */ } public void foo(int a1, int a2) { /* implementation */ } public void foo(int a1, int a2, int a3) { /* implementation */ } public void foo(int a1, int a2, int a3, int... rest) { /* implementation for 4+ arguments */ }
This optimization:
Remember: This is a performance optimization and should only be used when necessary.
What is an "inexact method reference" in Java, and why does it cause problems with overloaded methods?
An inexact method reference:
System.out::println
that could potentially refer to multiple overloaded methodsProblems with overloaded methods:
Example demonstrating the problem:
// These two methods accept different functional interfaces void process(Consumer<Object> c) { /* implementation */ } void process(Predicate<Object> p) { /* implementation */ } // This fails to compile even though it seems like it should work process(System.out::println); // ERROR - ambiguous // These work fine because the lambda form provides context process(x -> System.out.println(x)); // Calls process(Consumer) process(x -> {System.out.println(x); return true;}); // Calls process(Predicate)
This is a classic case where the overload resolution algorithm works differently than most developers would expect.
What technique should you use to ensure that overloaded methods with similar functionality behave identically when passed the same parameters?
To ensure consistent behavior across overloaded methods:
Use forwarding - have the more specific method forward to the more general method:
// Ensuring identical behavior by forwarding public boolean contentEquals(StringBuffer sb) { // Forward from specific to general implementation return contentEquals((CharSequence) sb); } public boolean contentEquals(CharSequence cs) { // Primary implementation here // ...implementation logic... }
This technique:
A common use case is when retrofitting existing classes to implement new interfaces or when evolving an API while maintaining backwards compatibility.
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:
How should you document module-level elements when using the Java module system?
When using the Java module system (introduced in Java 9):
module-info.java
fileExample:
/** * A module providing core utility classes for the application. * <p> * This module exports various utility packages and provides * implementations of the logging service. */ module com.example.utilities { exports com.example.utilities.collections; exports com.example.utilities.concurrency; provides java.util.logging.LoggingProvider with com.example.utilities.logging.LogProvider; }
Showing 10 of 56 cards. Add this deck to your collection to see all cards.