I recently stumbled upon this weird behaviour of Java where a variable of type Hashset, which is declared private in the parent class, is not unique for each instance of the child. Instead, all the instances are pointing to the same private variable and modifying for one instance is reflecting for all. However, It is unique if we have instances from two classes inherited from the same parent.
Please find the Class structure below.
Public Class Main { private Set<String> test = new HashSet(); protected void addToTest (String s){test.add(s);} } Public Class Child1 extends Main { } Public Class Child2 extends Main{ }
Main o1 = new Child1(); Main o2 = new Child1(); o1.addToTest("test"); o2.addToTest("test"); o1.clear(); //If we clear from one object is clearing at both the places, and both the object's hashcode of the variable test is same.
Main o1 = new Child1(); Main o2 = new Child2(); o1.addToTest("test"); o2.addToTest("test"); o1.clear(); //If we clear from one object is not clearing at both the places, and both the object's hashcode of the variable test is different
Can someone help me understand why the behaviour is different? Also, I was under the assumption that if a variable is declared private, it should be unique to that instance, but here it is not;
Why the instance variables are not unique here?
The behavior you are observing is related to the way inheritance and polymorphism work in Java. In Java, instance variables are not overridden like methods. They are hidden when a subclass declares a variable with the same name. The visibility and access rules are determined by the type of the reference, not the type of the object the reference is pointing to.
Let’s break down the scenarios you’ve mentioned:
Main o1 = new Child1(); Main o2 = new Child1();
In this case, both o1 and o2 are of type Main (the declared type). When you call o1.addToTest("test"), it adds the element to the test set of the Main class. Since both o1 and o2 have the same declared type, they share the same test set.
o1
o2
Main
o1.addToTest("test")
test
Main o1 = new Child1(); Main o2 = new Child2();
Here, o1 and o2 are of different types (Child1 and Child2), even though their declared type is Main. When you call o1.addToTest("test"), it adds the element to the test set of Child1. o2.addToTest("test") adds the element to the test set of Child2. Since o1 and o2 have different runtime types, their test sets are separate.
Child1
Child2
o2.addToTest("test")
In Java, instance variables are bound at compile-time based on the declared type of the reference variable. The declared type determines which members are accessible. This is known as early binding or static binding.
If you want to achieve polymorphic behavior where the actual type of the object determines the behavior, you should use methods (like your addToTest method). Methods can be overridden in subclasses, and the method called is determined at runtime based on the actual type of the object (dynamic binding). This is a fundamental principle of object-oriented programming known as polymorphism.
addToTest