3

Please let me know why the below class is Immutable as discussed in Java concurrency in practice - By Brian Goetz

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

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

As the class is not final, any class can extend it.But why it is still Immutable?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
Pramod Kishore
  • 307
  • 2
  • 8
  • Could have made it final, but that's be the only change. – duffymo Oct 12 '15 at 12:58
  • I think it must be clearer to say that Point interface (aka "abstraction") is not mutable (`x` and `y` attributes) whereas sub-classes can add additional states which can be mutable (or not). – LoganMzz Oct 13 '15 at 09:44

2 Answers2

6

It's immutable because once you construct an instance of it, you cannot change its internal state in any way. That's because it has no setters and the x and y are final i.e. you cannot mutate/change the x or y value.

EDIT (check that example):

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;
    }
}
peter.petrov
  • 38,363
  • 16
  • 94
  • 159
  • But any class can extend it and apply it's own getter and setter and use that class instead when point class is required. – Pramod Kishore Oct 12 '15 at 13:10
  • 1
    If so the sub-class is mutable but that does not mean that Point is mutable. Right? Make a few tests, think about them, and you'll see what I mean. – peter.petrov Oct 12 '15 at 13:11
  • Please follow the link .You will come to know my doubts.http://stackoverflow.com/questions/12306651/why-would-one-declare-an-immutable-class-final-in-java – Pramod Kishore Oct 12 '15 at 13:27
  • It's weakly immutable. If you do anything that relies on its immutability (e.g. a `Point` is passed to the constructor of another class, without a defensive copy, and then used for calculating that class's `hashCode`) - and you are passed a mutable subclass of it, your code may be broken. – RealSkeptic Oct 12 '15 at 13:32
  • 2
    @PramodKishore Note this comment there: "You don't need to make the entire class final just the getter... public final int getValue()". Here we have no getter and x,y are final. So we're covered IMHO. Read carefully all comments after templatetypedef's answer. – peter.petrov Oct 12 '15 at 13:35
  • Also note this comment, it says it all: " templatetypedef Sep 6 '12 at 19:45". "The method that you pass the Mutable object into won't change the object. The concern is that that method might assume that the object is immutable when it really isn't..." That's it. So `Immutable` is still immutable IMHO :) – peter.petrov Oct 12 '15 at 13:40
  • @peter.petrov Let's assume we have a class that extends Point class.public 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; } } as Point1 is an instance of Point , we can pass Point1 when Point is required.And then all the bad things will happen. – Pramod Kishore Oct 13 '15 at 07:36
  • @PramodKishore I don't see what bad can happen, you're missing something here. See that example which I added. Even if you mutate the instance, you're mutating the internal state of the Point1 part of it, not of the Point part of it. Note that in the method `consume` even such code as `if (p instanceof Point1)` will normally not be there, as this method knows nothing about Point1, it deals with Point only. But even if it's there - what is the problem? I don't see it. Point is still immutable. – peter.petrov Oct 13 '15 at 09:32
  • @peter public static void main(String[] args) { Map pointMap=new HashMap(); Point1 p1 = new Point1(4, 10); pointMap.put(p1, "x"); for(Point p:pointMap.keySet()){ Point1 P1=(Point1)p; System.out.println(P1.getX()); System.out.println(P1.getY()); } p1.setX(5);// A thread came along and changed the value p1.setY(9);// x and y for(Point p:pointMap.keySet()){ Point1 p2=(Point1)p; System.out.println(p2.getX()); System.out.println(p2.getY()); } } – Pramod Kishore Oct 13 '15 at 10:23
  • @PramodKishore The Point part of the object is still not changed. You are setting and getting and printing the x,y values from the Point1 part of your object, not from the Point part of it. This is what is confusing you. – peter.petrov Oct 13 '15 at 15:41
1

In simple terms – No, this class is not immutable. But things are more complicated than that.

In his book Brian Goetz says nothing about immutable classes. He talking about immutable objects. Object called immutable if:

  • its state cannot be modified after construction;
  • all its fields are final;
  • it's properly constructed (this reference doesn't escape during construction).

There is indeed no mention of final class requirement. But this is because we are talking about objects of given type (for example Point) at runtime. Objects can't change their type in runtime, so there is no need for final class requirement. If we know objects state doesn't change at runtime it's effectively immutable. In this sense objects of type Point are indeed immutable.

But note that in Listing 3.11 (Chapter 3.4 Immutablility) immutable object provided as an example, and its class defined as final.

The problem I thinks there is some confusion between immutable object and immutable type (class).

In reality for application to be correctly multithreaded, all clients should be aligned in terms of shared objects usage policy. This is where you need immutable types. Because regardless of Point objects immutability, there could be mutable, and as a consequence, non thread-safe Point subtypes. And if you have any, Liskov substitution principle is broken. Point subtypes should not degrade on provided guarantees.

For a type to be immutable it must obey following rules:

  • class state cannot be modified after construction;
  • all its fields are final;
  • it's properly constructed (this reference doesn't escape during construction);
  • all subtypes of a class should be immutable or class should be final;
  • all supertypes of a class should be immutable.

This is basically same restrictions as described by Brian Goetz, but in context of compile-time rather than runtime. If you accomplish this all objects created from those immutable-types will be thread-safe.

So, write immutable classes, and yes, Point type should be final.

Denis Bazhenov
  • 9,680
  • 8
  • 43
  • 65