不可变类字段修复建议

发布于:2025-08-01 ⋅ 阅读:(19) ⋅ 点赞:(0)

在设计类时,将字段设为不可变(immutable)可提升代码的健壮性和线程安全性。以下是修复可变字段为不可变的建议和步骤:


核心原则

  1. 无 Setter 方法:禁止提供修改字段的方法。

  2. 字段用 final 修饰:强制在构造时初始化。

  3. 防御性拷贝:对引用类型字段,构造/返回时进行深拷贝。

  4. 类本身不可变:避免子类破坏不可变性(如将类声明为 final)。


修复步骤与示例

1. 基础类型字段

直接添加 final,移除 setter 方法:

java

// 修复前(可变)
public class User {
    private int age; // 可变字段
    public void setAge(int age) { this.age = age; }
}

// 修复后(不可变)
public final class User {
    private final int age; // final 字段
    public User(int age) { this.age = age; } // 构造时初始化
    public int getAge() { return age; } // 无 setter
}
2. 引用类型字段(如数组、集合)
  • 构造时深拷贝

  • Getter 返回不可修改视图/拷贝

java

// 修复前(可变)
public class Data {
    private List<String> items; // 可变集合
    public void setItems(List<String> items) { this.items = items; }
}

// 修复后(不可变)
public final class Data {
    private final List<String> items;

    public Data(List<String> items) {
        this.items = new ArrayList<>(items); // 深拷贝传入集合
    }

    public List<String> getItems() {
        return Collections.unmodifiableList(items); // 返回只读视图
        // 或返回深拷贝:return new ArrayList<>(items);
    }
}
3. 自定义对象字段

确保引用的对象本身不可变:

java

public final class Address { // 被引用的类也需不可变
    private final String city;
    public Address(String city) { this.city = city; }
    public String getCity() { return city; }
}

public final class User {
    private final Address address; // 引用不可变对象
    public User(Address address) {
        this.address = new Address(address.getCity()); // 深拷贝
    }
    public Address getAddress() {
        return new Address(address.getCity()); // 返回拷贝
    }
}
4. 避免外部修改(防御性编程)
  • 如果字段是数组:

    java

    public final class ImmutableArray {
        private final int[] array;
    
        public ImmutableArray(int[] array) {
            this.array = Arrays.copyOf(array, array.length); // 深拷贝
        }
    
        public int[] getArray() {
            return Arrays.copyOf(array, array.length); // 返回拷贝
        }
    }

关键检查点

  1. ✅ 所有字段用 final 声明。

  2. ✅ 无 setter 方法

  3. ✅ 引用类型在构造时深拷贝外部数据。

  4. ✅ Getter 返回只读视图或深拷贝

  5. ✅ 类本身为 final(防止子类覆盖方法破坏不可变性)。


不可变类的优势

  • 线程安全:无需同步,天然线程安全。

  • 易于维护:状态在构造后永不改变。

  • 安全共享:可自由缓存、重用对象(如 String)。


注意事项

  • 深拷贝可能影响性能,需权衡场景。

  • 对复杂嵌套对象,确保整个引用链不可变。

  • 使用不可变集合库(如 Guava ImmutableList)简化实现。


网站公告

今日签到

点亮在社区的每一天
去签到