viernes, 5 de noviembre de 2021

Immutable Class Java

Immutable Class Java 


 

link https://github.com/jalbertomr/immutableClassLab.git

Why is necesary the immutability on classes. One reason of this requirement is that the state of the class (the value of their properties, fields) never change through any step of a series of procedures where the class has been used, these could be transactions by messages managed by other entites diferent to the one which create the original data of the class, Or simply on the internal procedures their data not be able to be changed by error by any one that uses and manipulate the class as DAO.

   Lets establish an example class which be representative and a little complex that has simple fields and collections and also an extended class to play with these to show the way of protect them agains change made from oneself or another coworker.





1.- Declare the class as final. (so can´t be extended ).

2.- Make all field private.

3.-  No setter methods for variables.

4.- Make all mutable fields final. (value can be assigned only once).

5.- Initialize all the fields via constructor. ( via deep copy).

6.- On getter methods, perform cloning rather than returning the actual object reference.


Why inmutable clases has to be final? Because subclases can add mutable behaviour hereding value from inmutable to an new mutable variable.

To test the immutability of the data in our program, specificaly in the class, let´s start with a SimpleClass and a class extended from it, in order to check the behaivor desired of immutability of his data or state. To Test different methods of initialize the class we use a case by an enum in the copy constructor. the methods will be 

public enum InitWays {
SHALLOW_COPY,
DEEP_COPY_WHILE,
DEEP_COPY_FOR_MAPENTRY,
DEEP_COPY_FOR,
DEEP_COPY_FUNCTIONAL,
CLONNING;
}

The SimpleClass properties with a representative types of properties

public class SimpleClass implements Cloneable{
private final InitWays INIT_WAY = DEEP_COPY_FUNCTIONAL;
private int id;
private BigDecimal bigDecimal;
private String name;
private Date date;
private HashMap<String, Date> sdMap = new HashMap<>();

their constructor of SimpleClass

public SimpleClass(int id, BigDecimal bigDecimal, String name, Date date, HashMap<String, Date> ssMap) {
this.id = id;
this.bigDecimal = bigDecimal;
this.name = name;
Object obj = date.clone();
this.date = (Date) obj;
String key;
switch (INIT_WAY) {
case SHALLOW_COPY:
this.sdMap = ssMap; // Shallow Copy
break;
case DEEP_COPY_WHILE: // Deep Copy
Iterator<String> iterator = ssMap.keySet().iterator();
while (iterator.hasNext()) {
key = iterator.next();
this.sdMap.put(key, ssMap.get(key));
}
break;
case DEEP_COPY_FOR_MAPENTRY:
for (Map.Entry<String, Date> mapEntry : ssMap.entrySet()) {
this.sdMap.put(mapEntry.getKey(), mapEntry.getValue());
}
break;
case DEEP_COPY_FOR:
for (String s : ssMap.keySet()) {
this.sdMap.put(s, ssMap.get(s));
}
break;
case DEEP_COPY_FUNCTIONAL:
this.sdMap = (HashMap)ssMap.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
break;
case CLONNING:
this.sdMap = (HashMap<String, Date>) ssMap.clone();
break;
}
}

Their copy constructor of SimpleClass

/**
* Copy Constructor
* @param simpleClass
*/
public SimpleClass( SimpleClass simpleClass) {
this.id = simpleClass.getId();
this.bigDecimal = simpleClass.getBigDecimal();
this.setName( simpleClass.getName());
Object obj = simpleClass.getDate().clone();
this.date = (Date)obj;

switch(INIT_WAY){
case SHALLOW_COPY:
this.sdMap = simpleClass.getSdMap();
break;
case DEEP_COPY_WHILE:
String key;
Iterator<String> iterator = simpleClass.getSdMap().keySet().iterator();
while (iterator.hasNext()) {
key = iterator.next();
this.sdMap.put(key, simpleClass.getSdMap().get(key));
}
break;
case DEEP_COPY_FOR:
for ( String s : simpleClass.getSdMap().keySet())
this.sdMap.put( s, simpleClass.getSdMap().get( s));
break;
case DEEP_COPY_FOR_MAPENTRY:
for (Map.Entry<String, Date> mapEntry : simpleClass.getSdMap().entrySet()){
this.sdMap.put( mapEntry.getKey(), mapEntry.getValue());
}
break;
case DEEP_COPY_FUNCTIONAL:
this.sdMap = (HashMap)simpleClass.getSdMap().entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
break;
case CLONNING:
this.sdMap = (HashMap<String,Date>) simpleClass.getSdMap().clone();
break;
}
}

The other methods includind the clone method, the getters and setters are obvied.

@Override
protected Object clone() throws CloneNotSupportedException {
SimpleClass sc = (SimpleClass) super.clone();
Object obj = sc.date.clone();
sc.date = (Date) obj;
Object obj2 = ((HashMap<String, Date>)sc.sdMap).clone();
sc.sdMap = (HashMap<String, Date>) obj2;
return sc;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SimpleClass)) return false;
SimpleClass that = (SimpleClass) o;
return getId() == that.getId() &&
getBigDecimal().equals(that.getBigDecimal()) &&
getName().equals(that.getName()) &&
getDate().equals(that.getDate()) &&
sdMap.equals(that.sdMap);
}

@Override
public int hashCode() {
return Objects.hash(getId(), getBigDecimal(), getName(), getDate(), sdMap);
}

@Override
public String toString() {
return "SimpleClass{" +
"id=" + id +
", bigDecimal=" + bigDecimal +
", name='" + name + '\'' +
", date=" + date +
", sdMap=" + sdMap +
'}';
}

 The SimpleClassExtended and their constructors with representative type of data

public class SimpleClassExtended extends SimpleClass implements Cloneable {
int idExtended;
String nameExtended;
Date dateExtended;

public SimpleClassExtended(int id, BigDecimal bigDecimal, String name, Date date, HashMap<String, Date> sdMap, int idExtended, String nameExtended, Date dateExtended) {
super( id, bigDecimal, name, date, sdMap);
this.idExtended = idExtended;
this.nameExtended = nameExtended;
Object obj = dateExtended.clone();
this.dateExtended = (Date)obj;
}

public SimpleClassExtended( SimpleClass simpleClass, int idExtended, String nameExtended, Date dateExtended) {
super( simpleClass);
this.idExtended = idExtended;
this.nameExtended = nameExtended;
Object obj = dateExtended.clone();
this.dateExtended = (Date)obj;
}

public SimpleClassExtended( SimpleClassExtended simpleClassExtended){
super( simpleClassExtended.getSimpleClass());
this.idExtended = simpleClassExtended.getIdExtended();
this.nameExtended = simpleClassExtended.getNameExtended();
Object obj = simpleClassExtended.getDateExtended().clone();
this.dateExtended = (Date) obj;
}

 Thier other methods, the getters and setters are obvied.

@Override
protected Object clone() throws CloneNotSupportedException {
SimpleClassExtended sce = ( SimpleClassExtended) super.clone();
Object obj = this.getDate().clone();
sce.setDate( (Date) obj);
return super.clone();
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SimpleClassExtended)) return false;
if (!super.equals(o)) return false;
SimpleClassExtended that = (SimpleClassExtended) o;
return getIdExtended() == that.getIdExtended() &&
getNameExtended().equals(that.getNameExtended()) &&
getDateExtended().equals(that.getDateExtended());
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), getIdExtended(), getNameExtended(), getDateExtended());
}

@Override
public String toString() {
return "SimpleClassExtended{" +
"idExtended=" + idExtended +
", nameExtended='" + nameExtended + '\'' +
", dateExtended=" + dateExtended +
'}';
}
}

 

We have a Main that plays with the data.

public class Main {

public static void main(String[] args) throws CloneNotSupportedException {

Map<Integer, String> hash_Map = new HashMap<Integer, String>();

// putting elements into the map
hash_Map.put(1, "primero");
hash_Map.put(2, "segundo");
hash_Map.put(3, "tercero");

HashMap<String, Date> sdMap = new HashMap<String, Date>();
sdMap.put("a",getDateObject("01/01/1971"));
sdMap.put("b",getDateObject("02/02/1972"));
sdMap.put("c",getDateObject("03/03/1973"));

SimpleClass simpleClass01 = new SimpleClass(1,new BigDecimal("1111.11"),"simpleClassConstructor", getDateObject("01/01/2020"), sdMap);

SimpleClass simpleClassBackup = new SimpleClass( simpleClass01);

SimpleClass simpleClassCloned = (SimpleClass) simpleClass01.clone();
System.out.println(" simpleClass01: " + simpleClass01);
System.out.println("--- Modifying state of simpleClass01---");
System.out.println("simpleClass01.getDate().getTime():" + simpleClass01.getDate().getTime() );
//simpleClass01.getDate().setTime( simpleClass01.getDate().getTime() + 1234567890L );
simpleClassBackup.getSdMap().put("a",getDateObject("06/06/2066"));
simpleClassCloned.getSdMap().put("a",getDateObject("07/07/2077"));
System.out.println("modified value of simpleClass01: " + simpleClass01);

System.out.println("simpleClass01.equals(simpleClassBackup): " + Boolean.toString( simpleClass01.equals( simpleClassBackup)).toUpperCase() );
System.out.println("simpleClass01.equals(simpleClassCloned): " + Boolean.toString( simpleClass01.equals( simpleClassCloned)).toUpperCase() );
System.out.println("value of simpleClass01 : " + simpleClass01);
System.out.println("value of simpleClassBackup: " + simpleClassBackup);
System.out.println("value of simpleClassCloned: " + simpleClassCloned);
System.out.println("-----------------------------");
System.out.println("--- SimpleClass change all value properties ---");
System.out.println( "original simpleClass01:" + simpleClass01);
simpleClass01.setId(0);
simpleClass01.setBigDecimal( simpleClass01.getBigDecimal().add( new BigDecimal("1111.11")));
simpleClass01.setName( simpleClass01.getName() + "+simpleClassSetName");
simpleClass01.setDate( getDateObject( "05/05/2055"));
System.out.println( "changed simpleClass01:" + simpleClass01);

System.out.println( "---- Working with inherited Class ----");
SimpleClassExtended simpleClassExtended = new SimpleClassExtended( simpleClass01,10,"simpleClassExtendedConstructor", getDateObject("06/06/1066"));
SimpleClassExtended simpleClassExtCloned = (SimpleClassExtended) simpleClassExtended.clone();
System.out.println("-- Initial Class values --");
System.out.println("simpleClass01: " + simpleClass01);
System.out.println("simpleClassExtended.getSimpleClass(): " + simpleClassExtended.getSimpleClass());
System.out.println("simpleClassExtended: " + simpleClassExtended);

System.out.println("-- Values before modify --");
System.out.println("simpleClass01: " + simpleClass01);
System.out.println("simpleClassExtended.getSimpleClass(): " + simpleClassExtended.getSimpleClass());
System.out.println("simpleClassExtended: " + simpleClassExtended);
System.out.println("simpleClassExtCloned: " + simpleClassExtCloned);
System.out.println("-- Modified field values of inherited class (mutable class)--");

simpleClassExtended.setId( simpleClassExtended.getId() + 1);
simpleClassExtended.setName("-simpleClassExtendedSetName");
simpleClassExtended.setBigDecimal( simpleClassExtended.getBigDecimal().add( new BigDecimal("1111.11")));
simpleClassExtended.getDate().setTime( simpleClassExtended.getDate().getTime() + 123456789L);

simpleClassExtCloned.setId( simpleClassCloned.getId() + 10);
simpleClassExtCloned.setName("-simpleClassCloned.setName");
simpleClassExtCloned.setBigDecimal( simpleClassCloned.getBigDecimal().add( new BigDecimal("99999.99")));
simpleClassExtCloned.getDate().setTime( simpleClassExtCloned.getDate().getTime() + 99999999L);

simpleClassExtended.setIdExtended( simpleClassExtended.getIdExtended() + 1);
simpleClassExtended.setNameExtended( simpleClassExtended.getNameExtended()+"-simpleClassExtended.setNameExtended");
System.out.println("simpleClass01: " + simpleClass01);
System.out.println("simpleClassExtended.getSimpleClass(): " + simpleClassExtended.getSimpleClass());
System.out.println("simpleClassExtended: " + simpleClassExtended);
System.out.println("simpleClassExtCloned.getSimpleClass(): " + simpleClassExtCloned.getSimpleClass());
System.out.println("simpleClassExtCloned: " + simpleClassExtCloned);
}

static Date getDateObject(String sDate) {
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
try {
return sdf.parse( sDate);
} catch (ParseException pe) {
pe.printStackTrace();
return null;
}
}

 The output with DEEP_COPY_FUNCTIONAL initialization of the class basicClass.

"C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\jbr\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\lib\idea_rt.jar=62575:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\bin" -Dfile.encoding=UTF-8 -classpath D:\proy\immutableClassLab\out\production\dataClasses com.bext.Main
                  simpleClass01: SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
--- Modifying state of simpleClass01---
simpleClass01.getDate().getTime():1577858400000
modified value of simpleClass01: SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
simpleClass01.equals(simpleClassBackup): FALSE
simpleClass01.equals(simpleClassCloned): FALSE
value of simpleClass01    : SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
value of simpleClassBackup: SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
value of simpleClassCloned: SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Wed Jul 07 00:00:00 CDT 2077, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
-----------------------------
--- SimpleClass change all value properties ---
original simpleClass01:SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
changed simpleClass01:SimpleClass{id=0, bigDecimal=2222.22, name='simpleClassConstructor+simpleClassSetName', date=Wed May 05 00:00:00 CDT 2055, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
---- Working with inherited Class ----
-- Initial Class values --
simpleClass01: SimpleClass{id=0, bigDecimal=2222.22, name='simpleClassConstructor+simpleClassSetName', date=Wed May 05 00:00:00 CDT 2055, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
simpleClassExtended.getSimpleClass(): SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtended: SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
-- Values before modify --
simpleClass01: SimpleClass{id=0, bigDecimal=2222.22, name='simpleClassConstructor+simpleClassSetName', date=Wed May 05 00:00:00 CDT 2055, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
simpleClassExtended.getSimpleClass(): SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtended: SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtCloned: SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
-- Modified field values of inherited class (mutable class)--
simpleClass01: SimpleClass{id=0, bigDecimal=2222.22, name='simpleClassConstructor+simpleClassSetName', date=Wed May 05 00:00:00 CDT 2055, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
simpleClassExtended.getSimpleClass(): SimpleClassExtended{idExtended=11, nameExtended='simpleClassExtendedConstructor-simpleClassExtended.setNameExtended', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtended: SimpleClassExtended{idExtended=11, nameExtended='simpleClassExtendedConstructor-simpleClassExtended.setNameExtended', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtCloned.getSimpleClass(): SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtCloned: SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}

Process finished with exit code 0

The output whit SHALLOW_COPY initialization of the class basicClass.

"C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\jbr\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\lib\idea_rt.jar=62640:C:\Program Files\JetBrains\IntelliJ IDEA 2019.2.3\bin" -Dfile.encoding=UTF-8 -classpath D:\proy\immutableClassLab\out\production\dataClasses com.bext.Main
                  simpleClass01: SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Fri Jan 01 00:00:00 CST 1971, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
--- Modifying state of simpleClass01---
simpleClass01.getDate().getTime():1577858400000
modified value of simpleClass01: SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
simpleClass01.equals(simpleClassBackup): TRUE
simpleClass01.equals(simpleClassCloned): FALSE
value of simpleClass01    : SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
value of simpleClassBackup: SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
value of simpleClassCloned: SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Wed Jul 07 00:00:00 CDT 2077, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
-----------------------------
--- SimpleClass change all value properties ---
original simpleClass01:SimpleClass{id=1, bigDecimal=1111.11, name='simpleClassConstructor', date=Wed Jan 01 00:00:00 CST 2020, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
changed simpleClass01:SimpleClass{id=0, bigDecimal=2222.22, name='simpleClassConstructor+simpleClassSetName', date=Wed May 05 00:00:00 CDT 2055, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
---- Working with inherited Class ----
-- Initial Class values --
simpleClass01: SimpleClass{id=0, bigDecimal=2222.22, name='simpleClassConstructor+simpleClassSetName', date=Wed May 05 00:00:00 CDT 2055, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
simpleClassExtended.getSimpleClass(): SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtended: SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
-- Values before modify --
simpleClass01: SimpleClass{id=0, bigDecimal=2222.22, name='simpleClassConstructor+simpleClassSetName', date=Wed May 05 00:00:00 CDT 2055, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
simpleClassExtended.getSimpleClass(): SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtended: SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtCloned: SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
-- Modified field values of inherited class (mutable class)--
simpleClass01: SimpleClass{id=0, bigDecimal=2222.22, name='simpleClassConstructor+simpleClassSetName', date=Wed May 05 00:00:00 CDT 2055, sdMap={a=Sun Jun 06 00:00:00 CDT 2066, b=Wed Feb 02 00:00:00 CST 1972, c=Sat Mar 03 00:00:00 CST 1973}}
simpleClassExtended.getSimpleClass(): SimpleClassExtended{idExtended=11, nameExtended='simpleClassExtendedConstructor-simpleClassExtended.setNameExtended', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtended: SimpleClassExtended{idExtended=11, nameExtended='simpleClassExtendedConstructor-simpleClassExtended.setNameExtended', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtCloned.getSimpleClass(): SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}
simpleClassExtCloned: SimpleClassExtended{idExtended=10, nameExtended='simpleClassExtendedConstructor', dateExtended=Tue Jun 06 00:00:00 CST 1066}

Process finished with exit code 0

 To simplify the test use the unit test

class SimpleClassTest {

HashMap<String, Date> sdMap = new HashMap<>();

@BeforeEach
void init(){
sdMap.put("a",getDateObject("01/01/1971"));
sdMap.put("b",getDateObject("02/02/1972"));
sdMap.put("c",getDateObject("03/03/1973"));
}

@Test
void testCopyConstructor(){
SimpleClass sc = new SimpleClass(1,new BigDecimal("1111.11"),"simpleClassConstructor", getDateObject("01/01/2020"), sdMap);
SimpleClass scCopyConstructor = new SimpleClass(sc);
assertEquals( sc, scCopyConstructor);
assertEquals( sc.getSdMap().get("b"), getDateObject("02/02/1972"));
assertEquals( sc.getSdMap(), scCopyConstructor.getSdMap());
}

@Test
void testIndependencyCopyConstructor(){
HashMap<String,Date> refMap = new HashMap<>();
refMap.put("a",getDateObject("06/06/2066"));
refMap.put("z",getDateObject("12/12/2012"));

SimpleClass sc = new SimpleClass(1,new BigDecimal("1111.11"),"simpleClassConstructor", getDateObject("01/01/2020"), sdMap);
SimpleClass scCopyConstructor = new SimpleClass(sc);

assertFalse(sc.getSdMap() == scCopyConstructor.getSdMap());
assertNotSame( sc.getSdMap(), scCopyConstructor.getSdMap());
scCopyConstructor.setId(2);
scCopyConstructor.setName("name");
scCopyConstructor.setBigDecimal( new BigDecimal("22222.22"));
scCopyConstructor.setDate( getDateObject( "02/02/2022"));
scCopyConstructor.setSdMap( refMap);

assertEquals( sc.getId(), 1);
assertEquals( sc.getName(), "simpleClassConstructor");
assertEquals( sc.getBigDecimal(), new BigDecimal("1111.11"));
assertEquals(sc.getDate(), getDateObject("01/01/2020"));
assertEquals( sc.getSdMap().get("a"), getDateObject("01/01/1971"));

assertEquals( scCopyConstructor.getId(), 2);
assertEquals( scCopyConstructor.getName(), "name");
assertEquals( scCopyConstructor.getBigDecimal(), new BigDecimal("22222.22"));
assertEquals( scCopyConstructor.getDate(), getDateObject("02/02/2022"));
}

@Test
void testClone() throws CloneNotSupportedException {
SimpleClass sc = new SimpleClass(1,new BigDecimal("1111.11"),"simpleClassConstructor", getDateObject("01/01/2020"), sdMap);
SimpleClass scCloned = (SimpleClass) sc.clone();

assertEquals( sc, scCloned);
assertNotSame( sc, scCloned);
assertEquals( sc.getDate(), scCloned.getDate());
assertFalse(sc.getDate() == scCloned.getDate());
assertEquals( sc.getSdMap(), scCloned.getSdMap());
assertFalse( sc.getSdMap() == scCloned.getSdMap());

sc.setId(2);
sc.setName("name");
sc.setBigDecimal( new BigDecimal("22222.22"));
sc.setDate( getDateObject( "02/02/2022"));

assertEquals( scCloned.getId(), 1);
assertEquals( scCloned.getName(), "simpleClassConstructor");
assertEquals( scCloned.getBigDecimal(), new BigDecimal("1111.11"));
assertEquals( scCloned.getDate(), getDateObject("01/01/2020"));
}
}

for the SimpleClassExtended

class SimpleClassExtendedTest {
SimpleClass sc;
HashMap<String, Date> sdMap = new HashMap<>();
Date dateExtended;

@BeforeEach
void init(){
sdMap.put("a",getDateObject("01/01/1971"));
sdMap.put("b",getDateObject("02/02/1972"));
sdMap.put("c",getDateObject("03/03/1973"));
dateExtended = getDateObject("01/01/2020");
sc = new SimpleClass(1,new BigDecimal("1111.11"),"simpleClassConstructor", getDateObject("01/01/2020"), sdMap);
}

@Test
void testCopyConstructor(){
SimpleClassExtended sce = new SimpleClassExtended( sc, 10, "nameExtended", dateExtended);
SimpleClassExtended sceCC = new SimpleClassExtended( sce);
assertEquals( sce, sceCC);
}

@Test
void testIndependencyCopyConstructor(){
SimpleClassExtended sce = new SimpleClassExtended( sc, 10, "nameExtended", dateExtended);
SimpleClassExtended sceCC = new SimpleClassExtended( sce);

sceCC.setId( 2);
sceCC.setName("sce");
sceCC.setBigDecimal( new BigDecimal("2222.22"));
sceCC.setDate( getDateObject("02/02/2022"));
sceCC.setIdExtended(200);
sceCC.setNameExtended("setNameExtended");

assertEquals( sce.getSimpleClass().getId(), 1);
assertEquals( sce.getSimpleClass().getName(), "simpleClassConstructor");
assertEquals( sce.getSimpleClass().getBigDecimal(), new BigDecimal("1111.11"));
assertEquals( sce.getSimpleClass().getDate(), getDateObject("01/01/2020"));
assertEquals( sce.getIdExtended(), 10);
assertEquals( sce.getNameExtended(),"nameExtended" );
assertNotSame( sce, sceCC);
}

@Test
void testClone() throws CloneNotSupportedException {
SimpleClassExtended sce = new SimpleClassExtended( sc, 10, "nameExtended", dateExtended);
SimpleClassExtended sceCloned = (SimpleClassExtended) sce.clone();

assertEquals(sce, sceCloned);
assertNotSame( sce, sceCloned);
}

 Running the test with SHALLOW_COPY class initialization.


Running the test with DEEP_COPY_FUNCTIONAL

 


eot