【问题标题】:Is it a good practice to use the return of a function where the returned value is an object supplied as an argument? [closed]使用返回值是作为参数提供的对象的函数的返回是否是一种好习惯? [关闭]
【发布时间】:2015-04-10 06:52:48
【问题描述】:

我只是想问一下这是否是一个好的 java 实践,或者有更好的方法(官方方式)来做同样的事情。

首先,我需要更新一些hashmap信息:

Map<Date, Object> rows = new HashMap<Date, Object>();

这是 excel 行的对象,每个日期(即 10 月 1 日、10 月 2 日等)和包含该行信息的对象。

所以,为了获取这些信息,我有一些方法,例如:

rows = performQuery1(rows, foo, bar, initDate, endDate);
rows = performQuery2(rows, someDAO, foo);

还有……

private HashMap<Date, Object> performQuery1(rows, foo, bar, Date, Date) {
  // Some code that adds or removes elements from the hashmap "rows"
  rows.put(date1, o1);

  //Then return the same object
  return rows;
}

所以我的问题是:这是一个好的 Java 实践吗?

rows = performQuery1(rows, foo, bar, initDate, endDate);
rows = performQuery2(rows, someDAO, foo);

还是不行?

【问题讨论】:

  • 这个问题太宽泛了,这个问题没有很好的答案。但是,在这种情况下,完全没有必要返回 HashMap
  • 所以,不是“rows = performQuery1(...)”,而是“performQuery1(...)”,performQuery1 将是“private void”,对吗?
  • 这不是programmers.stackexchange.com的问题吗?
  • 不是您确切问题的答案,但将复杂对象作为地图中的键一种不好的做法。

标签: java performance hashmap theory


【解决方案1】:

这个问题确实非常广泛,或者,关注“最佳实践”部分,可能基于意见 - 但不是主要基于意见,因为这样的模式有一个有效的论据。

通常您有一种方法可以在某处获取数据,并且应该将其放入目标数据结构(在您的情况下可能是集合或地图)。

这种方法的 签名 有几个选项(大致在您的示例中,但这种模式可以推广)。

第一个可能是

/**
 * Computes ... something, and returns the result as a map
 */
Map<Date, Result> execute(Date d0, Date d1) { ... }

第二个可能是

/**
 * Computes ... something, and places the results into the
 * given map
 */
void execute(Date d0, Date d1, Map<Date, Result> results)  { ... }

但是,为了获得最大的灵活性,我经常提到第三个选项(这是您实际询问的那个):

/**
 * Computes ... something, and places the results into the
 * given map, which is then returned. If the given map is
 * null, then a new map will be created and returned.
 */
Map<Date, Result> execute(
    Date d0, Date d1, Map<Date, Result> results)  { ... }

这有几个优点:

  • 您可以方便地让调用创建一个新地图:

    Map<Date, Result> results = execute(d0, d1, null);
    
  • 您可以确定目标数据结构的实现。例如,如果您总是返回新地图,则无法在 HashMapLinkedHashMap 之间进行选择。将目标数据结构传递给方法允许您调用

    Map<Date, Result> results = 
        execute(d0, d1, new HashMap<Date, Result>());
    

    Map<Date, Result> results = 
        execute(d0, d1, new LinkedHashMap<Date, Result>());
    

    分别

  • 您不必为每次通话都创建新地图。例如,您可以创建一系列调用

    Map<Date, Result> results = new HashMap<Date, Result>();
    execute(d0, d1, results);
    execute(d2, d3, results);
    

    在给定的地图中累积结果


考虑到它可以轻松地模拟这两种选择,这种方法的威力可能会变得更加明显:

class DB {

    // The private method that can emulate both public methods:
    private Map<Date, Result> executeImpl(
        Date d0, Date d1, Map<Date, Result> results);

    // The implementation that returns a new map
    public Map<Date, Result> execute(Date d0, Date d1) {
        return executeImpl(d0, d1, null);
    }

    // The implementation that fills a given map
    public void execute(Date d0, Date d1, Map<Date, Result> results) {
        executeImpl(d0, d1, results);
    }

}

顺便说一句:Java SDK 的某些地方也使用了类似的模式。比如在不同的应用案例中:BufferedImageOp#filter:

BufferedImage filter(BufferedImage src, BufferedImage dest)

... 如果目标图像为空,则创建具有适当 ColorModel 的 BufferedImage。

返回:过滤后的 BufferedImage

【讨论】:

  • 哇!非常感谢你,马可!
  • 答案很好,而且绝对比我的要好 :-) 我只想补充一点,恕我直言,您的组合选项的缺点是有点违反直觉,并且在不阅读文档的情况下难以正确阅读和使用 - 这可能超过在许多情况下它的好处。
【解决方案2】:

问题的核心归结为Is Java "pass-by-reference" or "pass-by-value"?

此外,您应该返回一个新创建的对象或修改由参数引用的对象,但不能同时返回。有一些有用的例外 像Method chaining,但这里似乎不是这样。

当您从方法返回数据时,程序员会期望数据是新创建的对象,而通过参数引用的对象保持不变。让方法返回 void(或成功代码)提示程序员该方法修改了参数引用的对象而不是返回新对象,并使您的代码更易于阅读。

【讨论】:

  • 我认为java不会通过引用传递参数
  • @AleksandrM 感谢您指出我在回答中遗漏的区别。我在回答中链接了这个问题。我仍然认为答案的重要部分(您应该将引用作为参数传递或返回新对象)成立。如果不是这种情况,请告诉我。
猜你喜欢
  • 1970-01-01
  • 2023-04-05
  • 2018-10-20
  • 1970-01-01
  • 2015-02-18
  • 2017-12-09
  • 2023-02-15
  • 1970-01-01
  • 2012-05-07
相关资源
最近更新 更多