【问题标题】:In Java Concurrency in Practice by Brian Goetz, Why point class is Immutable which is used by DelegatingVehicleTracker在 Brian Goetz 的 Java Concurrency in Practice 中,为什么点类是不可变的,它被 DelegatingVehicleTracker 使用
【发布时间】:2015-10-13 17:32:31
【问题描述】:

请告诉我为什么下面的类是不可变的,正如 Java 并发实践中所讨论的那样 - 作者:Brian Goetz

@Immutable
public class Point {
    public final int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

由于类不是final,任何类都可以扩展它。但是为什么它仍然是不可变的?

【问题讨论】:

  • 本可以完成,但这是唯一的变化。
  • 我认为必须更清楚地说 Point 接口(又名“抽象”)是不可变的(xy 属性),而子类可以添加其他可以可变的状态(或不)。

标签: java concurrency immutability


【解决方案1】:

它是不可变的,因为一旦你构造了它的一个实例,你就不能以任何方式改变它的内部状态。那是因为它没有设置器,并且 x 和 y 是最终的,即您不能改变/更改 x 或 y 值。

编辑(查看该示例):

package test;

public class Test002 {

    public static void main(String[] args) {
        Point1 p1 = new Point1(4, 10);
        consume(p1);
    }

    public static void consume(Point p){
        System.out.println("=============");
        System.out.println(p.x);
        System.out.println(p.y);

        if (p instanceof Point1){
            System.out.println("=============");
            Point1 p1 = (Point1)p;
            p1.setX(5);
            p1.setY(11);
            System.out.println(p.x);
            System.out.println(p.y);
            System.out.println(p1.getX());
            System.out.println(p1.getY());          
        }
    }

}



class Point {
    public final int x, y;

    public Point(int x, int y) {
        this.x = x;
        this.y = y;
    }
}

class Point1 extends Point {
    private int x;
    private int y;

    public Point1(int x, int y) {
        super(x, y);
        this.x = x;
        this.y = y;
    }

    public void setX(int x) {
        this.x = x;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }
}

【讨论】:

  • 但是任何类都可以扩展它并应用它自己的 getter 和 setter,并在需要点类时使用该类。
  • 如果是这样子类是可变的,但这并不意味着 Point 是可变的。正确的?做一些测试,思考一下,你就会明白我的意思了。
  • 请点击链接。你会知道我的疑惑。stackoverflow.com/questions/12306651/…
  • 它是弱不可变的。如果您做任何依赖于其不变性的事情(例如,Point 被传递给另一个类的构造函数,没有防御性副本,然后用于计算该类的 hashCode) - 并且您被传递给它的可变子类,您的代码可能已损坏。
  • @PramodKishore 请注意这里的评论:“您不需要使整个类最终成为 getter ... public final int getValue()”。这里我们没有 getter,x,y 是最终的。因此,恕我直言。仔细阅读 templatetypedef 回答后的所有 cmets。
【解决方案2】:

简单来说——,这个类不是不可变的。但事情比这更复杂。

Brian Goetz 在他的书中没有提及不可变类。他在谈论不可变对象。在以下情况下称为不可变对象:

  • 构造后无法修改其状态;
  • 它的所有字段都是最终的;
  • 构造正确(this 引用在构造过程中不会转义)。

确实没有提到final 类要求。但这是因为我们在运行时讨论给定类型的对象(例如Point)。对象不能在运行时改变它们的类型,所以不需要final 类要求。如果我们知道对象状态在运行时不会改变,那么它实际上是不可变的。从这个意义上说,Point 类型的对象确实是不可变的。

但请注意,在清单 3.11(第 3.4 章 Immutablility)中提供的不可变对象作为示例,其类定义为final

我认为不可变对象不可变类型(类)之间存在一些混淆的问题。

实际上,要使应用程序正确地进行多线程处理,所有客户端都应在共享对象使用策略方面保持一致。这就是你需要不可变类型的地方。因为不管Point 对象的不变性如何,都可能存在可变的,因此,非线程安全的Point 子类型。如果有的话,里氏替换原则就被打破了。 Point 子类型不应因提供的保证而降级。

对于不可变的类型,它必须遵守以下规则:

  • 构造后不能修改类状态;
  • 它的所有字段都是最终的;
  • 构造正确(this 引用在构造过程中不会转义);
  • 类的所有子类型都应该是不可变的,或者类应该是final
  • 一个类的所有超类型都应该是不可变的

这与 Brian Goetz 描述的限制基本相同,但在编译时而不是运行时的上下文中。如果你做到了这一点,从这些不可变类型创建的所有对象都将是线程安全的。

所以,写不可变类,是的,Point 类型应该是final

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-28
    • 2023-03-26
    • 2018-06-07
    • 2016-02-05
    • 1970-01-01
    • 1970-01-01
    • 2021-09-12
    相关资源
    最近更新 更多