重构以输入参数作为返回载体的方法:寻求简洁优化方案
Great question—mutating input parameters like this is a classic readability and maintainability trap. Side effects (unexpectedly modifying the caller's data) make code hard to reason about, especially when the same collections are used elsewhere. Here are a few clean, scalable refactoring options that avoid creating a one-off wrapper class for every method:
1. Use a Java Record (Java 16+) – The Most Concise Option
If you're on Java 16 or newer, Records are perfect here. They let you define an immutable data carrier in a single line, no boilerplate getters, constructors, or equals/hashCode needed.
Instead of modifying input lists, create new ones inside the method and return them along with your count in a Record:
// Define a reusable (or one-off) Record to hold all results record AttemptsResult(int count, List<Object> bar, List<Object> baz) {} AttemptsResult getAttempts(int foo) { int count = 5; // Create new lists instead of mutating inputs List<Object> bar = new ArrayList<>(); bar.add(new Object()); // Add your actual bar-populating logic here List<Object> baz = new ArrayList<>(); baz.add(new Object()); // Add your actual baz-populating logic here return new AttemptsResult(count, bar, baz); }
When calling the method, you'll get all values explicitly:
AttemptsResult result = getAttempts(someFooValue); int attempts = result.count(); List<Object> populatedBar = result.bar(); List<Object> populatedBaz = result.baz();
This makes it crystal clear what the method produces, with zero side effects on your original collections.
2. Custom Immutable Result Class (Pre-Java 16)
If you're stuck on an older Java version, a lightweight immutable class works just as well. Keep it simple—you don't need fancy frameworks, just a plain class with final fields and a constructor:
public class AttemptsResult { private final int count; private final List<Object> bar; private final List<Object> baz; public AttemptsResult(int count, List<Object> bar, List<Object> baz) { this.count = count; // Wrap lists in unmodifiable collections to prevent external mutation this.bar = Collections.unmodifiableList(new ArrayList<>(bar)); this.baz = Collections.unmodifiableList(new ArrayList<>(baz)); } // Getters for access public int getCount() { return count; } public List<Object> getBar() { return bar; } public List<Object> getBaz() { return baz; } }
The unmodifiable wrappers add an extra layer of safety, ensuring the caller can't accidentally modify the lists returned by your method.
3. Split Methods by Responsibility (Single Responsibility Principle)
If the count calculation and list population don't need to be tightly coupled, split them into separate methods. This makes each method's purpose obvious and reusable:
// Handles only the count logic int getAttemptsCount(int foo) { return 5; // Replace with your actual count calculation } // Handles only bar population List<Object> populateBar(int foo) { List<Object> bar = new ArrayList<>(); bar.add(new Object()); // Add your bar-populating logic here return bar; } // Handles only baz population List<Object> populateBaz(int foo) { List<Object> baz = new ArrayList<>(); baz.add(new Object()); // Add your baz-populating logic here return baz; }
Now the caller can pick and choose which methods to use, and there's no ambiguity about what each method does.
Why These Work Better Than Your Original Approach
All these options eliminate side effects:
- No more unexpected modifications to the caller's input collections
- Code behavior is explicit—you can see exactly what data is produced by each method
- They're easier to test, since you don't have to set up and track mutable input state
内容的提问来源于stack exchange,提问作者ng.newbie

