You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

发送数据的防御性拷贝最佳方式及Effective Java规则39实现疑问

Defensive Copying for Data Transfer: Best Practices & Redundancy Checks

Great question! Let's unpack this, since defensive copying can feel counterintuitive at first—especially when you start seeing multiple copies pop up in your code. First, let's tie this back to the core of Effective Java Item 39: defensive copying exists to protect a class's internal state from being modified unexpectedly by external code. Whether multiple copies are necessary depends on your design goals and the mutability of the data class.

Why Your Code Has Two Copies (And If They're Redundant)

Let's break down your example step by step:

  1. SomeClass.getData() returns a copy: This is correct! Since MyData is mutable (it has a setName() method), returning a direct reference to someClass's internal myData would let external code call setName() and modify SomeClass's private state—breaking encapsulation. This copy is non-negotiable if you want to protect SomeClass's integrity.
  2. OtherClass constructor makes a second copy: This one depends on what OtherClass needs. If OtherClass wants to hold its own independent copy of MyData (so external changes to the original passed-in instance don't affect OtherClass's internal state), this copy is necessary. But if OtherClass only needs to use the data temporarily, or you can guarantee the passed-in MyData instance won't be modified elsewhere, this copy is redundant.

The Optimal Fix: Make MyData Immutable

The cleanest way to eliminate redundant copying (and simplify your code entirely) is to turn MyData into an immutable class. Immutable objects are inherently safe to share—you never need to copy them, because their state can't be changed after creation. This aligns perfectly with Effective Java's emphasis on preferring immutable classes when possible.

Here's how to refactor your code:

// Immutable MyData class: no setters, all fields final, no mutable state
final class MyData {
    private final String name;

    public MyData(String name) {
        this.name = name;
    }

    // Only a getter, no setter to modify state
    public String getName() {
        return name;
    }

    // No need for a copy constructor anymore!
}

public class SomeClass {
    private final MyData myData;

    SomeClass() {
        myData = new MyData("1");
    }

    // Safe to return the direct reference—MyData can't be modified
    public MyData getData() {
        return myData;
    }

    public static void main(String[] args) {
        SomeClass someClass = new SomeClass();
        OtherClass otherClass = new OtherClass(someClass.getData());
    }
}

class OtherClass {
    private final MyData myData;

    // Safe to assign directly—no risk of external modification
    OtherClass(MyData data) {
        this.myData = data;
    }
}

If MyData Must Stay Mutable

If you can't make MyData immutable (e.g., it needs to support state changes), you can optimize the copying based on your needs:

  • Keep SomeClass.getData()'s copy: This is still required to protect SomeClass's internal state.
  • Skip OtherClass's copy only if: You don't care if external code modifies the MyData instance that OtherClass holds. For example, if OtherClass only reads the data once and doesn't store it long-term, you can avoid the second copy. But if OtherClass needs a stable, unmodifiable copy of the data, keep the second copy.

Key Takeaways

  • Defensive copying isn't about "copying every time you pass data"—it's about protecting your class's internal state.
  • Immutable classes are the gold standard for safe data transfer: they eliminate the need for copying entirely while maintaining encapsulation.
  • If mutability is unavoidable, only copy when necessary to prevent external code from altering your class's state.

内容的提问来源于stack exchange,提问作者myoldgrandpa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:27:23