【问题标题】:Java setting private fields inside constructorsJava 在构造函数中设置私有字段
【发布时间】:2013-08-03 15:27:42
【问题描述】:

常见的设计实践是将实例变量设为私有,并让公共 getter 和 setter 访问它们。但是很多时候,我在互联网上看到代码示例具有直接将值分配给私有实例变量的构造函数,而不是在构造函数中使用设置器。我错过了什么吗?

public class Person{
    private String name;

    public Person(String name){
        //is this right, seems like the whole encapsulation purpose is defeated
        this.name = name;

        //shouldn't this be used
        setName(name);
    }

    public String getName(){
        return this.name;
    }

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

【问题讨论】:

  • 我相信将变量设为私有的目的是将它们与其他类的直接操作隔离开来。
  • 另外,getter不需要this关键字;它仅在存在歧义时使用(即,如果在同一范围内有另一个名为“name”的变量)

标签: java constructor private-members


【解决方案1】:

你没有错过任何东西。你做什么完全取决于你的情况。但是,请考虑一下:

在 setter 中进行参数验证是很常见的。例如,假设我有一个类,其字段可以保存 0 到 10 的值(下面的异常类型不需要“抛出”,但为了清楚起见,我将其包括在内):

public class Example {
    private int value; 
    public Example () {
    }
    public final int getValue () {
        return value;
    } 
    public final void setValue (int value) throws IllegalArgumentException { 
        if (value < 0 || value > 10)
            throw new IllegalArgumentException("Value is out of range.");
    }
}

在这里,setValue() 验证“值”以确保它遵守规则。我们有一个不变量声明“一个超出范围值的示例将不存在”。现在假设我们要创建一个带值的构造函数。你可以这样做:

public class Example {
    ...
    public Example (int value) {
        this.value = value;
    }
    ...
}

如您所见,有一个问题。语句 new Example(11) 会成功,现在存在一个违反我们规则的示例。但是,如果我们在构造函数中使用 setter,我们也可以方便地将所有参数验证添加到构造函数中:

public class Example {
    ...
    public Example (int value) throws IllegalArgumentException {
        setValue(value); // throws if out of range
    }
    ...
}

所以这样做有很多好处。

现在,仍然存在您可能希望直接赋值的情况。一方面,也许您没有可用的 setter(尽管我认为创建私有或包私有 setter 仍然是可取的,出于上述原因,如果需要,可以支持反射/bean,并且便于在更复杂的代码中进行验证)。

另一个原因可能是您的构造函数以某种方式提前知道将分配有效值,因此不需要验证并且可以直接分配变量。不过,这通常不是跳过使用 setter 的令人信服的理由。

但是,总而言之,尽可能在任何地方使用 setter 通常是一个好主意,这通常会导致代码更清晰、更清晰,并且随着复杂性的增加更容易维护。

您看到的大多数直接设置变量的示例只是人们“懒惰” - 如果情况允许,这是完全可以接受的(也许您正在编写一个快速测试程序或应用程序并且不想实现例如,一堆二传手)。只要您牢记大局并仅在适当的时候“懒惰”,这没有任何问题。

我想根据此处的其他一些答案添加一些内容:如果您在子类中覆盖了 setter,并且您正在设置的数据破坏了基类假定的不变量,那么应该制作相关的 setter final 或基类不应该做出这些假设。 如果重写设置器破坏了基类不变量,那么手头就有更大的问题。

您会注意到上面示例中的 getter/setter 是 final 的。这是因为我们的规则是“任何示例必须具有 0 到 10 之间的值”。因此,该规则扩展到子类。如果我们没有该规则,并且 Example 可以采用任何值,那么我们就不需要 final setter 并且可以允许子类覆盖。

希望对您有所帮助。

【讨论】:

    【解决方案2】:

    有时当你想让类不可变时,这只是你需要做的事情之一。并且在这种情况下根本没有 setter 方法。

    【讨论】:

      【解决方案3】:

      根据上下文,使用getter和setter实际上比在构造函数中使用成员变量更违反封装。如果你想设置这个类的成员变量'name',这些方法中的任何一种都可以工作,因为构造对调用者是隐藏的,因此不会违反封装。一个警告是,在构造函数中使用 setName 可能会调用子类中的覆盖方法,这可能不是您想要的(因为它可能会在超类中留下未定义的名称)。

      这里有一个与您类似的问题,可能会提供更多见解:

      calling setters from a constructor

      【讨论】:

        【解决方案4】:

        私有变量可以在类中的任何地方直接访问

        settng variabels private 是从其他类中封装它们

        【讨论】:

          【解决方案5】:

          将变量设置为私有是为了鼓励其他类的封装。

          除非 setName(String) 打算做一些额外的事情(方法名称并不暗示),否则在私有变量所在的类中没有必要使用 setter。

          【讨论】:

          • 这不是完全正确的,因为您可以将所有验证逻辑封装在一个 setter 中,从而在您的班级的其他地方获得验证服务。
          【解决方案6】:

          这不会破坏封装,因为私有成员仍然对其他类隐藏

          如果修饰符方法不包含任何逻辑,只是设置成员,那么直接设置成员调用其setter方法没有区别,尽管为了更好的实践应该调用setter。

          setter 表示此人的姓名将来可能会更改,并且可以轻松地进行更改,而无需再次创建整个 person 对象。

          【讨论】:

            【解决方案7】:

            在构造函数中初始化变量是一种非常常见的做法。它可用于根据用户调用的构造函数为变量赋值。您不能基于客户端代码将调用 setter 方法为实例变量赋值的假设编写代码。在创建对象时(即在构造函数内部)为其分配默认值始终是安全的。

            在构造函数中初始化变量和根据调用代码的要求将其设置为不同的值(使用setter方法)之间存在差异。两者都有不同的目的和目标。

            【讨论】:

            • 我认为这不能回答问题(或者我可能误解了)。当您已经提供了 setter 时,OP 不会询问在构造函数中初始化变量是否合适;相反,OP 是在构造函数中询问上述设置器的 use
            【解决方案8】:

            这是完全正常的。一些变量可能需要在创建对象后立即初始化,因此在构造函数中传递它们是有意义的,而且很多时候我们可能不想为这些变量提供设置器以避免在创建对象后更改值。

            【讨论】:

              【解决方案9】:

              在类中直接赋值是可以的,提供的setter不做任何其他处理。

              基本上,setter/getter 用于提供对私有数据的限制性访问,例如返回数据的副本而不是私有对象的引用、验证 getter 中的数据等。

              既然构造函数是对象本身的一部分,而且我们确信我们所做的事情是正确的,那么就可以了。

              【讨论】:

              • 值得指出的是,这里的关键声明是“我们确信我们所做的是正确的”。如果您曾经对字段的值施加规则,请不要忘记注意确保构造函数不会违反这些规则。
              【解决方案10】:

              我的首选方法(如 Joshua Bloch 在“Effective Java”中所述)是使构造函数私有,使字段成为最终字段(即完全消除 setter),并要求客户端使用 Builder Pattern 获取实例或Factory Method Pattern,它将负责任何必要的验证以保护不变量。然后(私有)构造函数将简单地将给定参数(已经由 Builder 或 Factory Method 验证)分配给适当的字段,即privatefinal

              【讨论】:

                猜你喜欢
                • 2010-10-09
                • 2013-10-29
                • 2017-03-02
                • 2018-05-23
                • 2017-03-28
                • 2011-10-11
                • 1970-01-01
                • 1970-01-01
                • 2013-06-24
                相关资源
                最近更新 更多