【问题标题】:Is it ok and safe to work with references passed to constructor - Java使用传递给构造函数的引用是否可以且安全 - Java
【发布时间】:2016-12-04 10:54:09
【问题描述】:

我有以下代码:

public class Triangle {

    private Point left;
    private Point right;
    private Point top;

    public Triangle(Point left, Point right, Point top) {
        this.left = left;
        this.right = right;
        this.top = top;
    }

    // more program logic...
}

我想知道构造这样的对象是否可行且安全,因为我担心 Point 类型的三个变量中的某些变量可以从外部修改(破坏封装)。 例如:

public static void main(String[] args) {

    Point left = new Point(0.0, 1.0);
    Point right = new Point(2.4, 3.2);
    Point top = new Point(5.8, 2.0);

    Triangle t = new Triangle(left, right, top);

    top.setX(10.2);
    top.setY(23.4); 
}

这无疑将操纵在 Triangle 变量中引用的同一个“顶部”对象。 修复在 Triangle 构造函数中执行以下操作也是如此:

public Triangle(Point left, Point right, Point top) {
    this.left = new Point(left);
    this.right = new Point(right);
    this.top = new Point(top);
}

(请记住,我在Point类中有一个复制构造函数,所以上面的三个语句是有效的)

【问题讨论】:

    标签: java reference encapsulation


    【解决方案1】:

    您可以在构造函数中克隆原始点并保持克隆对外界隐藏。如果Point 已经实现了Cloneable 或者你可以自己实现,那么这样使用:

    public Triangle(Point left, Point right, Point top) {
        this.left = left.clone();
        this.right = right.clone();
        this.top = top.clone();
    }
    

    如果Point 没有实现Cloneable 并且您无权访问其源代码,则只需手动克隆点:

    public Triangle(Point left, Point right, Point top) {
        this.left = new Point(left.getX(), left.getY());
        this.right = new Point(right.getX(), right.getY());
        this.top = new Point(top.getX(), top.getY());
    }
    

    【讨论】:

    • Point 没有实现 Cloneable,但我已经制作了一个复制构造函数,它接受一个 Point 并基本上基于另一个 Point 构造一个 Point,类似于您的第二个建议。
    【解决方案2】:

    这是一个很好的问题。

    拥有可变状态并不总是一件坏事。例如,您可以将此 Triangle 对象实例发送到显示程序,如果您更改点坐标,它可以在屏幕上为三角形设置动画。

    这取决于用例和它应该解决的问题。

    如果您下定决心,对于您的用例,整个对象图(此对象、我的孩子和孩子的孩子等)一旦创建就应该是不可变的,有办法确保这一点。

    为了使不可变对象真正不可变,这里有一些很好的指导: http://www.javapractices.com/topic/TopicAction.do?Id=29

    如果对象具有集合作为属性,您可以根据需要使用不可变或不可修改的集合: Java Immutable Collections

    最后,如果您确实允许可变状态,并且想要跟踪,您可以将子对象注册为观察者。 When should we use Observer and Observable

    同样,原则是尽可能少地保持可变状态。换句话说,始终尽可能保持对象不可变。不可变集合很容易获得并且易于使用。问题在于我们的自定义类。

    如果您使自定义对象/模型对象完全不可变,您可能会遇到复制整个对象图以更改单个属性的情况。

    所以,保守一点。尤其是集合,这很容易做到。

    使用面向对象的编程范式很难避免可变性。函数式编程应该更符合不变性。您可能想看看 Haskell 或 Scala 只是为了体验一下。

    https://softwareengineering.stackexchange.com/questions/232711/complete-immutability-and-object-oriented-programming

    【讨论】:

    • 是的,您绝对正确,这取决于您希望应用程序如何工作。感谢您的彻底回复!
    【解决方案3】:

    如果你有一个复制构造函数,那么你可以安全地使用它。否则,如果点实现cloneable,则可以使用克隆。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-03-19
      • 1970-01-01
      • 2017-05-19
      • 1970-01-01
      • 1970-01-01
      • 2011-12-04
      相关资源
      最近更新 更多