简体   繁体   中英

On the static final keywords in Java

According to the tutorial :

The static modifier, in combination with the final modifier, is also used to define constants. The final modifier indicates that the value of this field cannot change.

I would agree with this only if the types involved were primitive. With reference types, eg an instance of a class Point2D where its position attributes were not final (ie, we could change its position), the attributes of this kind of variables such as public static final Point2D A = new Point2D(x,y); could still be changed. Is this true?

Yes, it can be changed. Only the references cannot be changed, but its internal fields can be. The following code shows it:

public class Final {
    static final Point p = new Point();
    public static void main(String[] args) {
        p = new Point(); // Fails
        p.b = 10; // OK
        p.a = 20; // Fails
    }
}

class Point {
    static final int a = 10;
    static int b = 20;
}

Groovy (an alternative JVM language) has an annotation called @Immutable , which blocks changing to a internal state of an object after it is constructed.

Correct, it can still be changed. The "static final", in this case, refers to the reference itself, which cannot be changed. However, if the object that it is references is mutable, then the object that it references can be changed.

An immutable object, such as a String, will be a constant.

public static final Point2D A = new Point2D(x,y);

Here the reference A is final and not the values inside the class Point2D .

You cannot do this after defining A static final:

//somewhere else in code
A = new Point2D(x1,y1);

public static final Point2D A = new Point2D(x,y); could still be changed. Is this true?

Reference of A could not be changed but its true associated Object's value can be changed if attributes are not final.

class Point2D{
  private int x;
  private int y;
  <getter & setter>
} 
class Constant{
  Public static final Point2D A = new Point2D(1,2);
  public static void main(String[] args){
     A.setX(10); // this will work 
     A = new Point2D(10,20);// this will not work
  }
}

In case Point2D's attributes are final then Point2D class would be immutable .

or you can send the clone-able object.

Like -

 private static final Point2D A = new Point2D(x,y);
 public static getA(){
     return A.clone();
 }

It's true. The final modifier is not transitive in any possible way.

It only means the reference will not change. The content of the reference (the fields of the object) may change over time.

The reference to the point ( A in your case) cannot change. Only the state of the object can change. So you can not create a new Point2D and assign it to the variable.

Only the reference is final, the object that is referenced can be changed (unless it is an immutable Object, like Integer or the like). So yes, it is only constant for a given value of "constant".

Yes.

Of course, you can also change the value of the final field later as described elsewhere .

You can find a very similiar question here with quite good explaination:

HERE: Why can final object be modified?

By the way I started thinking about Reflection mechanism ....

in theory ... I belive you can get owning class instance.. then get class members, find current instance and check it is final ....

I didn't check it and I dont't even kwnow if it's possible - it's just an idea (maybe ?)

in

public class Final {
    static final Point p = new Point();

    public static void main(String[] args) throws MyImmutableException {
        p = new Point(); // Fails
        p.setB(10); // OK
        p.setA(20); // Fails - throws MyImmutableException
    }
}       

public class Point() {
    int a = 10;
    int b = 20;

    public setA(int a) {
        this.a = a;
    }
    public setB(int b) throws MyImmutableException {
        detectIsFinal()
        this.b = b;
    }

    private void detectIsFinal() throws MyImmutableException {
        int mod = this.getClass().getModifiers()
        if (Modifier.isFinal(mod)) {
            throw new MyImmutableException();
        }
    }   
}

public class MyImmutableException extends Exception { 
    public MyImmutableException() {super(); }
}

I was thinking what else you can do ... again it's just a !!!!!!!!! PSEUDOCODE !!! I am not sure if it will even work :P

Maybe someone with annotations knowledge will be inspired and make it work. Unfortunatelly I don't have more time this week. Maybe later I will try to make a POC.

public class Final {
    @Immutable
    static final Point p = new Point();

    public static void main(String[] args)  {
        p = new Point(); // Fails
        p.setB(10); // Fails
        p.setA(20); // OK
    }
}       

public class Point() {
    int a = 10;
    @ImmutableOnThisInstance
    int b = 20;

    @DetectIsImmutable
    public setA(int a) {            
        this.a = a;
    }

    @DetectIsImmutable
    public setB(int b) {
        this.b = b;
    }      
}


class Immutable {
    ?????????????????
}

class DetectIsImmutable {
    /**
     * Just a pseudocode - I don't know how to work with ANNOTATIONS :) :) :)
     * SORRY :)
     * @throws MyImmutableException
     */
    private void detectMethod() throws MyImmutableException {
        Immutable instance = CLASS_THAT_IS_ANNOTATED.getClass().getAnnotation(Immutable.class)
        if (instance != null) {
            // get parent Method invocation name (from stacktrace list)
            String methodName = .............;
            if (methodName.startsWith("set")) {
                // check id we have variable with this settername
                String fieldName = ...; // cut "set" get rest of it, make first letterSmall 
                // find this field in object fields
                Field f = this.getClass().getDeclaredField(fieldName);
                if (f.getAnnotation(ImmutableOnThisInstance.class) != null) {
                    throw MyImmutableException();
                }
            }
        }
    }           

}

It is still true.

There is a way in which the claim you quote is true. static final describes a variable , and it is true that that variable cannot change, and so is a constant. The variable is a pointer to an object, and that object can change. But that doesn't stop the variable being a constant.

That is a less powerful way of being true that you can achieve with const in C++, but it's something.

Yes you are correct. The reference cannot be changed, but the object that is referenced can be. Something like this is perfectly legal:

public static final List<Object> someList = new ArrayList<Object>();

// ...

someList.add(someThing); // <-- this would be illegal if the referenced object was also constant

You can change the properties of Point2D, but not create a new instance of it.

I should also mention Point2D is abstract so you must create an instance of a child class that extends this.

As other already mentionned, it is the reference to your Point2D object that is static final, not the attributes x and y. If you want to make sure you cannot change the position of a Point2D object you should set attributes x and y static and final (initialized) in Point2D class.

static final Point2D A = new Point2D(x,y);

All it says is reference of class Point2D can't be changed.

            _____________
           |             |
A----------|--->(x,Y)    |
           |             | 
           |_____________|Heap

So you can't change A by pointing to a different object but of course you can change the value of (x,y) or the content of Point Object

If you want to make your object also as constant you will have to make Point object immutable.

When a reference variable is declared as final, you can't re-assign a new object to it once it is referring to an object. But, you can change the state of an object to which final reference variable is pointing. Look at the below example..

class A
{
    int i = 10;
}

public class UseOfFinalKeyword
{
    public static void main(String[] args)
    {
        final A a = new A();  //final reference variable

        a.i = 50;
        //you can change the state of an object to which final reference variable is pointing

        a = new A();  //compile time error

        //you can't re-assign a new object to final reference variable
    }
}

For more info, follow the link : final keyword in java

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM