类中声明方法的首选方式

5
我在类中创建方法以设置信息方面有疑问。
  1. creating separate methods for setting each attribute

    class Address{
        private String name;
        private String city;
    
        public setName(String name) { ... }
        public setCity(String name) { ... }
    }
    
  2. creating single method for setting all attributes

    class Address{
        private String name;
        private String city;
    
        public setAddress(String name,String city) { ... }
    }
    
从内存角度来看,以上两种方式哪种更可取?

2
请用代码示例展示您所说的两种方法。 - Jim Garrison
这个类的目的是什么?它是一个值对象吗?您需要单独设置每个值吗?这些值在对象创建后必须更改吗?您所说的“内存视角”是什么意思?人类记忆还是计算机内存? - buritos
使用构造函数。 - BalusC
1
如果你真的需要可变对象,那么getter/setter也可以。然而,不可变对象越来越普遍:https://dev59.com/TnE85IYBdhLWcg3wSxgv - SyntaxT3rr0r
+1 SyntaxT3rr0r。我完全同意这种情绪。 - Mansoor Siddiqui
7个回答

4
常见做法是使用JavaBean风格。
class Address {
  private String name;
  private String city;

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

  public String getName() {
     return name;
  }

  public setCity(String city){
     this.city = city;
  }

  public getCity() {
    return city;
  }

另一种常见的做法,与您第二个方法非常相似,是创建不可变对象。参数通过构造函数传递,而不是通过大型的setter方法。

class Address {
  private final String name;
  private final String city;

  public Address(String name, String city) {
      this.name = name;
      this.city = city;
  }

  public String getName() {
     return name;
  }

  public getCity() {
    return city;
  }
}

从内存的角度来看,第二个例子在构造函数中设置所有属性,而且这些属性都是不可变的。通常情况下,用这种方式构造的对象在被多个线程使用时更加安全。

在第二个例子中,不需要同步处理。而当多个线程使用标准的JavaBean对象时,则需要处理同步/内存问题。


3

不推荐使用第二种方法

第二个例子不推荐使用,因为如果您向Address类中添加新字段,则您是将其添加到现有的setter方法中还是创建一个新的setter方法?如果将其添加到现有的setter方法中,则调用该方法的任何类都将被破坏。如果创建一个新的setter方法,则对于想要使用该类的人来说,为什么某些字段以这种方式分组而其他字段则不是会感到困惑。

为要公开的每个字段使用单独的setter方法

常见的做法是为要公开的每个字段在类中拥有一个单独的setter方法(即第一个示例)。是否这是一种好的做法存在争议,因为它强制使一个类可变。如果可能,最好使对象不可变,原因有很多,详见此处

使用构造函数初始化字段

使类不可变的一种方式是去掉setter方法,而是通过类构造函数设置字段,如下所示。以这种方式实现的缺点是,如果您的类有很多字段,可能会导致构造函数调用变得很长,难以阅读。

public class Address {
    public String name;
    public String city;

    private Address(String name, String city) {
        this.name = name;
        this.city = city;
    }
}

使用构建器模式初始化您的字段

下面是一个完全不同的实现(灵感来自于这篇文章),它是构建器模式的一种变体。它模拟了对象的可变性而不牺牲可读性。

public class Address {
    public String name;
    public String city;

    private Address() {}

    private void setName(String name) {
        this.name = name;
    }

    private void setCity(String city) {
        this.city = city;
    }

    static class Builder {
        private Address address = new Address();

        public Builder name(String name) {
            address.setName(name);
            return this;
        }

        public Builder city(String city) {
            address.setCity(city);
            return this;
        }

        public Address build() {
            return address;
        }
    }
}

使用上述类,您可以按照以下方式创建Address类的不可变实例:
Address address = new Address.Builder()
        .name("Mansoor's address")
        .city("Toronto")
        .build();

哪种方法使用的内存更多?

从内存角度来看,由于类在内存中的大小取决于类中的字段,因此不应该有任何区别。 由于所有三种实现都具有相同的字段,无论使用哪种方法,它们都应该占用相同的内存空间。


这里是关于编程的内容翻译:它并不只是表示我在询问setter方法... 告诉我任何方法。 - satheesh
在我的答案中评论了“一般方法”的方面。 - aioobe
我不同意你的答案。为什么每个字段都要有setter是很常见的做法呢?在一个类中,往往会有一些内部字段是不想公开的。我所知道的唯一普遍采用这种做法的地方就是在Java Bean中。 - user425367
@Farmor 这种做法在Java bean中是很常见的,但许多非bean类使用setter而不是构造函数参数。只需看一下Spring API(例如org.springframework.jdbc.core.JdbcTemplate),这是很常见的用法。Spring也大量使用非POJO类的setter。 - Mansoor Siddiqui
还有一个评论更符合我的反对意见,而不是关于初始化字段的语义。假设我在我的类中有一个描述对象状态的字段。状态既不会在构造函数参数中设置,也不会在setter中设置。它将由构造函数初始化并通过常规方法更新。但它将是类内部的,并且不以任何方式公开,既不作为构造函数参数,也不通过setter或getter公开。 - user425367
显示剩余5条评论

2

我看不出这两种方法在内存方面有任何区别。

选择在类的接口中最有意义的方法。

我建议只有当两个属性在逻辑上密切相关或者存在某个类不变量时,才采用第二种方法,或者你不想暂时打破某个类不变量时。

对于你的Address示例,我肯定会选择两个setter方法,因为在谈论地址时,姓名和城市是完全不相关的。


对于一般的方法,我认为将一个方法拆分成两个对内存消耗影响很小。每个对象并没有自己的一组方法分配。包含方法的内存在类的所有实例之间共享。


经验法则:努力使您的类接口清晰而合乎逻辑。


1

这不是一个清晰的问题。你的意思是,你更喜欢像setFoo(String)setBar(int)这样的两种方法,还是像setFooBar(String, int)这样的一种方法?这取决于它们是否是逻辑上不同的属性,如果是,那么你需要单独的方法,或者它们经常(或仅)在一起设置才有意义。你可以提供两者。

两者都不会对内存产生任何影响。


1
JavaBean标准是为每个属性设置getter和setter:http://en.wikibooks.org/wiki/Java_Programming/Java_Beans。如果您不想遵循这个标准约定,那么最适合您的商店就是这样。根据本主题中其他答案的说法,可能存在最小的内存差异,如果有的话。

1

毫无疑问,第一点。

你不需要手写代码,只需声明字段。

然后让Eclipse为您完成其余工作。

在Eclipse中使用Source --> generate getters and setters。

与#2非常相似的构造在对象构造函数中完成。

关于内存的更新问题。对于这两种方式之间的内存差异,在生产代码中不必担心一秒钟。


数字1是常见的做法,但它会强制一个类变为可变的。 - Mansoor Siddiqui
1
你的意思是什么?setAddress("London", "Uk") 和 [setName("Uk") setCity("London")] 不都是可变的吗? - user425367
我想说的是,两种方法都不是优选。 :) - Mansoor Siddiqui

0
通常,您为每个属性编写一个setter和getter方法。
我并没有真正看到只使用一个方法来设置所有属性的情况。在这种情况下,所有属性应该具有相同的值吗?或者您总是需要为所有属性传递参数。这两种情况都不是您想要的。因此,您应该明确地优先选择第一种方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接