简体   繁体   中英

Why final instance class variable in Java?

If instance variable is set final its value can not be changed like

public class Final {

    private final int b;

    Final(int b) {
        this.b = b; 
    }

    int getFinal() {
        return  b = 8;  // COMPILE TIME ERROR 
    }
}


Somewhere in code I have seen instance class variable HashMap declared as final

 private  final Map<String, Object> cacheMap = new HashMap<String, Object>();

I could not understand why it is declared so? Normally in which case it is declared. Does it mean if once I put in hash map then I could not change its value?

Edit:
If cacheMap which is declared as final is passed as parameter to another class then error is not shown for final if I change its reference. Why it is so?

 class CacheDTO {

    private Map conditionMap;

    public Map getConditionMap() {
        return conditionMap;
    }

    public void setConditionMap(Map conditionMap) {
        this.conditionMap = conditionMap;
    }
}

Then

private  final Map<String, Object> cacheMap = new HashMap<String, Object>();
CacheDTO cc = new CacheDTO();
cc.setConditionMap(cacheMap);
Map<String, Object> cacheMapDeclaredAsFinal = cc.getConditionMap();
Map<String, Object> newMap = new HashMap<String, Object>();
cacheMapDeclaredAsFinal = newMap;    // In this case no error is shown. Though cacheMapDeclaredAsFinal reference is obtained by calling cc.getConditionMap() and cacheMapDeclaredAsFinal refers to final.

You can't change the Basket. Still you can change the fruits inside.

From Language specification # chapter 14.12.4

Once a final variable has been assigned, it always contains the same value. If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object.

When you declare a field or reference final, you must set the value once by the time the constructor exits.

You can assign a value to that variable only in constructor.

 private  final Map<String,Object> CacheMap = new HashMap<String,Object>();

here you can do

CacheMap.put(.....  

with in the class.

but you cannot do

CacheMap =   something.  //compile error.

You should know the difference between value and reference .

Edit

Here

 Map<String, Object> cachemapdeclaredasfinal = cc.geConditionMap();

 Map<String, Object> newMap = new HashMap<String, Object>();

 cachemapdeclaredasfinal  = newMap; // In this case no error is shown

Reason ,

Since cachemapdeclaredasfinal is not a new map it's another reference of conditionMap

when you create a new instance like this

   Map<String, Object> cachemapdeclaredasfinal =
                                new HashMap<String, Object>(cc.geConditionMap());

That error disappears. since you used new .

Edit 2 :

 private Map conditionMap;

 public void setConditionMap(Map ConditionMap) {
        this.conditionMap = conditionMap;
    }
  private  final Map<String, Object> CacheMap = new HashMap<String, Object>();
  CacheDto cc = new CacheDto();
  cc.setConditionMap(CacheMap);
  Map<String, Object> cachemapdeclaredasfinal = cc.geConditionMap();
  Map<String, Object> newMap = new HashMap<String, Object>();
 cachemapdeclaredasfinal  = newMap;

Here you what you confused is.

You are assigning one final declared map to some normal(non final ) map . When you retrieved that normal only you are getting and that not final so you can use/ assign it further.

In Short

normalMap= finalMap; //no error since normalMap is not final
finalMap =normalMap;// compiler error since normalMap is final

final has nothing to do with the contents of the object the variable is referring to. You will only not be able to change the value of the variable and make it refer to another object.

I think you are getting confused between "final" and "immutable" objects..

public class Final {

     private final int b;

     Final(int b) {
            this.b = b; 
     }

     int getFinal() {
          return  b = 8;  // COMPILE TIME ERROR 
     }
}

Final means you cannot change the reference to the object. In case of primitives, it means you cannot change the value. So, when you try to set b to 8, you get a compile time error.

cc.setConditionMap(cacheMap);

public void setConditionMap(Map conditionMap) {
        this.conditionMap = conditionMap;
}

In Java - "References to objects are passed by value" (As Bruce Eckel puts it in his book "Thinking in Java"). So, you are passing a copy of the reference. So, you have 2 references to the same cacheMap now.

So, you can change the cacheMap using any of the references. But you can reassign only the "copied" reference to another object, as it is not final (not the original one, the original one is final and CANNOT point to another object).

As the other answers have specified, you cannot make a final variable refer to another object.

Quoting from the Java Language Specification :

4.12.4. final Variables

A final variable may only be assigned to once... If a final variable holds a reference to an object, then the state of the object may be changed by operations on the object, but the variable will always refer to the same object.

That rule isn't being violated in the edited portion of your question:

  • You've declared CacheMap as final, and you're not reassigning a new value to it anywhere . If you'd be able to do that, it would be a violation.

  • cachemapdeclaredasfinal only refers to the same thing that CacheMap is referring to, and is not final itself.

As Suresh has mentioned upthread, it would help if you read up on values and references in Java. A good starting point is this thread: Is Java "pass by reference"? . Make sure you understand why Java is always pass-by-value and never pass-by-reference - that's the reason why the "finalness" of CacheMap wasn't getting passed around.

It means that the hasmap cannot be changed. The elements inside the hashmap are not tied to the final delimiter.

private static final HashMap<K, V> map = new HashMap<K, V>();

public void foo(K k, V v) {
    map.push(k, v);  //This is allowed
    map = new HashMap<K, V>  //This is not allowed
}

This question might help you: http://www.stackoverflow.com/questions/40480/is-java-pass-by-reference

From what I understand, this is what is actually happening: When you pass an object into a method with java, you're basically passing a pointer to the object. As such, when you call cc.geConditionMap(), you're basically getting the pointer back. When you change it, you're not actually changing the object. You are making your copy of the pointer point to a different map.

Your copy of the pointer isn't protected by final since you stored the copy to a non-final variable.

In "C/C++" terms:

Thing * a;
Thing * const b;
Thing const * c;
Thing const * const d;

The "final" in Java is closest to "b". "b" is a constant pointer to a Thing. "b" cannot be changed to point to a different Thing, but the Thing itself may be changed.

Java doesn't have a representation for "c" and "d". "c" is a pointer to a constant Thing. "c" may point to other Things, but the Things it points to cannot be changed (at least, not through "c" itself)

"d" combines "b" and "c": "d" is a constant pointer to a constant Thing.

Oh, and "a" of course is just nothing special.

Hm...In Java, not everything is an object so the rules are a little different.

final int f = 9;

Which, in C is much like

int const f = 9;

Which means you cannot change "f" or its integer value.

NOTE:

int const f;
const int g;

both mean the same thing, but "f" IMHO has clearer meaning. "g" is unfortunately very common.

This are the usages of the keyword final that I know:

In the class declaration

This means that the class: cannot be sub classed

public final class SomeClass {
//...
}

In a global variable

This means that once a value is assigned to it, it cannot change.

public class SomeClass  {
     private final int value = 5;
}

There will be a compile error if you don't assign the value, but what you can do is use composition to give a value.

public class SomeClass  {
     private final int value;
     public SomeClass(int value) {
       this.value=value
      }
}

In an object parameter

This means that the passed object cannot be changed

public class SomeClass {
   public void someMethod(final String value) {
      //...
   }
}

In local variables

This means that the value cannot change once assigned

public class SomeClass {
   public void someMethod(final String value) {
      final double pi = 3.14;
   }
}

In methods

This means that the method cannot be overriden

 public class SomeClass {
           public final void someMethod() {
              //...
           }
        }

In collections & maps

This means that the collection cannot be reinitialized, but it doesn't mean that the elements are inmutable , each of the elements will not be afected by the keyword final

 public class SomeClass {
       final HashMap<K, V> someMap = new HashMap<K, V>();
  }

When a reference is final, it cannot be linked to any other object.

The values of the object can be changed, so you can add values to the map, but cannot change the object of the map.

Let's say, final Map map = new HashMap(); new : is responsible to create object in heap which holds value

"map" reference will be created in stack which is final.

The value of "map" reference is real object created in heap.
As "map" reference is final, it can not have any other value in it.

When we pass "map" reference, Actually we pass the value of map which is nothing but reference of object created in heap. In the called method, another
reference "map" will be created in stack which holds the same reference of object in heap.

The same concept is coded in this example

import java.util.HashMap; import java.util.Map;

public class FinalExample { public static void main(String[] args) {

    // Please see this example in case of normal variable and go through the
    // comment
    Final1 f1 = new Final1();
    f1.fun2();

    // Please see this example in case of Map Object and go through the
    // comment
    Final2 f2 = new Final2();
    f2.fun2();

}

}

class Final1 { final int a = 10;

void fun1(int a) {
    a += 20;
    System.out.println(a);
}

void fun2() {

    // Here we are passing just content of final variable "a" but not the
    // block "a" itself.

    // When method fun1 is called another local block "a" will be created
    // This local "a" has nothing to do with instance final "a". Both are
    // different
    // We can change the value of local a it has nothing to do with instance
    // "a"

    fun1(a);
}

}

class Final2 { final static Map map = new HashMap();

static {
    map.put("1", "Nandeshwar");
    map.put("2", "Sah");
}

void fun1(Map map) {
    map.put("3", "John");
    map.put("4", "Nash");

    System.out.println(map);
}

void fun2() {

    // Here (in fun1) we pass the content of final map. The content of final
    // map is
    // the refernece of real object which holds the value
    // "1" "Nandeshwar // "2" "Sah".

    // When we call fun1, Another object "map(Map)" will be created. this
    // newly created object "map" will also
    // indicate the same reference as instance map refers

    // So the local object "map" and instance object "map" both is
    // different. But indicates the real Object which holds the value
    fun1(map);
}

}

Note:- When you declare an object reference as final, it means that it will always point to the same object on heap to which it was initialized.

If cacheMap which is declared as final is passed as parameter to another class then error is not shown for final if I change its reference. Why it is so?

1.) Java is pass by value so when you write cc.setConditionMap(cacheMap ) where cacheMap is an object reference (Note:- cacheMap is not an object itself, it is just a reference) then you are just passing the address of the object on heap to setConditionMap(cacheMap) , now inside setConditionMap(cacheMap) , conditionMap is initialized with the value of cacheMap (the value of cache Map is the address of Map Object). Now after this step both conditionMap and cacheMap refers to the same object on heap. But the catch is that you can again set the value of conditionMap so that it points to some other map object on heap but cacheMap will always point to the same Map Object.

2.) Declaring a variable as final doesn't means that object on heap is final(doesn't makes sense at all, right?) instead what it means is that it variable will always point to the object to which it was initialized and nobody can change it.

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