Java 在void方法中更改值或返回新对象,哪种方法是正确的?

Java 在void方法中更改值或返回新对象,哪种方法是正确的?,java,immutability,Java,Immutability,当您需要修改地图/列表或对象时,您更喜欢哪一种方法,哪一种方法更好 维护 在处理无效方法或新对象创建时,是否存在维护困难 成本 是的,新的对象创建仍在功能范围内,将可供GC使用,但如果我们在整个项目中这样做,想知道它会对应用程序产生怎样的影响,从一些有经验的人那里听到可能会很好 可测试性 使用void方法很难在调用方法中进行测试,但是如果能听到关于void和new的想法,那就太好了 健壮性 使用一种新的方法似乎可以避免副作用,而且更可靠。你的想法是什么 假设我有一个从方法返回

当您需要修改地图/列表或对象时,您更喜欢哪一种方法,哪一种方法更好

  • 维护
    • 在处理无效方法或新对象创建时,是否存在维护困难
  • 成本
    • 是的,新的对象创建仍在功能范围内,将可供GC使用,但如果我们在整个项目中这样做,想知道它会对应用程序产生怎样的影响,从一些有经验的人那里听到可能会很好
  • 可测试性
    • 使用void方法很难在调用方法中进行测试,但是如果能听到关于void和new的想法,那就太好了
  • 健壮性
    • 使用一种新的方法似乎可以避免副作用,而且更可靠。你的想法是什么
假设我有一个从方法返回的映射

public Map<Integer, User> getUsers() {
    Map<Integer, User> names = getNames();
    return names;
}
publicmap getUsers(){
映射名称=getNames();
返回姓名;
}
所以在同一个函数中,我需要为它们添加前缀

public Map<Integer, User> getUsers() {
    Map<Integer, User> names = getNames();
    addPrefixes(names);
    return names;
}
publicmap getUsers(){
映射名称=getNames();
添加前缀(名称);
返回姓名;
}
从不变性的角度来看,哪一个更好?我应该这样做吗

public Map<Integer, User> getUsers() {
    Map<Integer, User> names = getNames();
    Map<Integer, User> prefixedNames = getPrefixed(names);
    return prefixedNames;
}
publicmap getUsers(){
映射名称=getNames();
Map prefixedNames=getPrefixed(名称);
返回前缀名称;
}

如果是这样,如何实现getPrefixed函数,深度复制每个用户并将其添加到新地图并返回,您能举个例子吗?

让我们看看代码:

public Map<Integer, User> getUsers() {
    Map<Integer, User> names = getNames();
    // addPrefixes or names=getPrefixes
    return names;
}
现在是复制的时候了,因为任何用户都可能损坏对象的状态。但是,即使
getNames
是私有字段并返回内部字段,如果有人调用
getUsers
(一次、两次,…),会发生什么情况?前缀将添加到内部字段中

因此,我假设
getNames
返回名称的副本(例如,从数据库检索)。局部变量
names
是您的,是名称的工作副本

现在,您有三种选择:

void addPrefixes(Map<Integer, User> names) { // 1
    // update names
}

Map<Integer, User> getPrefixed(Map<Integer, User> names) { // 2
    // update names
    return names;
}

Map<Integer, User> getPrefixed(Map<Integer, User> names) { // 3
    // create copy, e.g. use Java8 stream;
    return namesCopy;
}
作者:

成本 如果功能是应用程序的瓶颈,则可以从选项3(更安全)开始,然后切换到选项2(带注释)。方案1和方案2的成本相同

测试性 如果需要测试前缀是否正确添加,请将
getPrefixed
作为小型
前缀器
类的公共方法(单一责任原则)

健壮性 选项3是最好的。如果修改
getNames
以返回内部字段,会发生什么情况。例如:

// before
public Map<Integer, User>() getNames() { 
    // query the db
    // create the map
    return names; 
}

// after: the method was to slow, cache the result
public Map<Integer, User>() getNames() { 
    if (this.names == null) {
        // query the db
        // create the map
    }
    return this.names; 
}
//之前
public Map()getNames(){
//查询数据库
//创建地图
返回姓名;
}
//之后:方法是减慢速度,缓存结果
public Map()getNames(){
if(this.names==null){
//查询数据库
//创建地图
}
返回此名称;
}
选项1和2将损坏字段
名称


重要提示:无论您决定什么,都要记录您的选择:当修改参数时,您应该让用户知道,因为最初的期望(在Java中)是参数不会被修改。如果函数返回副本,请让用户知道。

getNames()
方法
public
?(是的,这会影响我的答案。)这要看情况而定。现在还不清楚你的具体情况。改变空洞函数中的状态会产生副作用,并可能增加复杂性,特别是在并行计算中。创建新对象并通过函数返回它可以避免线程之间的依赖关系,但可能会降低性能,特别是在单线程情况下。关于访问器方法:我认为将内部集合作为不可变的方式返回是很好的,“如果”公开内部集合是必需的:<代码>返回集合。代码>如果多次调用getUsers(),数据是否正确?如果您不使用不变性,恐怕会在名称后面附加多个前缀
return getPrefixed(names);
return prefixer.getPrefixed(names);
// before
public Map<Integer, User>() getNames() { 
    // query the db
    // create the map
    return names; 
}

// after: the method was to slow, cache the result
public Map<Integer, User>() getNames() { 
    if (this.names == null) {
        // query the db
        // create the map
    }
    return this.names; 
}