Comparable interface and Cloneable interface

Article directory

  • 1. Comparable interface
  • 2. Clonable interface and deep copy

1, Comparable interface

The following is a program that implements sorting an array of objects:

class Student {<!-- -->
private String name;
private int score;
public Student(String name, int score) {<!-- -->
this.name = name;
this.score = score;
}
@Override
public String toString() {<!-- -->
return "[" + this.name + ":" + this.score + "]";
}
}

Given an array of student objects, sort the elements in this object array (in descending order of scores).

Student[] students = new Student[] {<!-- -->
new Student("张三", 95),
new Student("李思", 96),
new Student("王五", 97),
new Student("Zhao Liu", 92),
};

According to our previous understanding, we have a ready-made sort method for arrays. Can we use this method directly?

Arrays.sort(students);
System.out.println(Arrays.toString(students));
// An error occurred during operation and an exception was thrown.
Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable

If you think about it carefully, it is not difficult to find that, unlike ordinary integers, two integers can be directly compared, and the size relationship is clear. But how to determine the size relationship between two student objects? We need to specify additionally.
You can let the Student class implement the Comparable interface and implement its compareTo method.

class Student implements Comparable {<!-- -->
private String name;
private int score;
public Student(String name, int score) {<!-- -->
this.name = name;
this.score = score;
}
@Override
public String toString() {<!-- -->
return "[" + this.name + ":" + this.score + "]";
}
@Override
public int compareTo(Object o) {<!-- -->
Student s = (Student)o;
if (this.score > s.score) {<!-- -->
return -1;
} else if (this.score < s.score) {<!-- -->
return 1;
} else {<!-- -->
return 0;
}
}
}

Then compare the size relationship between the current object and the parameter object (calculated as a fraction).

If the current object should be sorted before the parameter object, a number less than 0 is returned.
If the current object should come after the parameter object, a number greater than 0 is returned.
If the current object and the parameter object are in no particular order, return 0.

Execute the program again and the results will be as expected.

//Execution results
[[Wang Wu:97], [Li Si:96], [Zhang San:95], [Zhao Liu:92]]

Note: For the sort method, each object in the array that needs to be passed in is “comparable” and needs to have the ability to compareTo. By overriding the compareTo method, you can define comparison rules.
In order to further deepen our understanding of the interface, we can try to implement a sort method ourselves to complete the sorting process just now (using bubble sorting).

public static void sort(Comparable[] array) {<!-- -->
for (int bound = 0; bound < array.length; bound + + ) {<!-- -->
for (int cur = array.length - 1; cur > bound; cur--) {<!-- -->
if (array[cur - 1].compareTo(array[cur]) > 0) {<!-- -->
//Explain that the order does not meet the requirements, exchange the positions of the two variables
Comparable tmp = array[cur - 1];
array[cur - 1] = array[cur];
array[cur] = tmp;
}
}
}
}

Execute the code again.

sort(students);
System.out.println(Arrays.toString(students));
// Results of the
[[Wang Wu:97], [Li Si:96], [Zhang San:95], [Zhao Liu:92]]

2, Clonable interface and deep copy

There are some useful interfaces built into Java, and Clonable is one of them. There is a clone method in the Object class. Calling this method can create a “copy” of the object. However, in order to legally call the clone method, you must first implement the Clonable interface, otherwise a CloneNotSupportedException exception will be thrown.
as follows:

class Animal implements Cloneable {<!-- -->
private String name;
@Override
public Animal clone() {<!-- -->
Animal o = null;
try {<!-- -->
o = (Animal)super.clone();
} catch (CloneNotSupportedException e) {<!-- -->
e.printStackTrace();
}
return o;
}
}
public class Test {<!-- -->
public static void main(String[] args) {<!-- -->
Animal animal = new Animal();
Animal animal2 = animal.clone();
System.out.println(animal == animal2);
}
}
//output result
// false

There are two concepts: Shallow copy and deep copy.
The object copied from Cloneable is a shallow copy.
Observe the following code:

class Money {<!-- -->
public double m = 99.99;
}
class Student implements Cloneable{<!-- -->
String name;
int age;
public Money money = new Money();
@Override
protected Object clone() throws CloneNotSupportedException {<!-- -->
return super.clone();
}
}
public class demo {<!-- -->
public static void main(String[] args) throws CloneNotSupportedException {<!-- -->
Student stduent1 = new Student();
Student stduent2 = (Student) Student.clone();
System.out.println("Result before modification through stduent2");
System.out.println(stduent1.money.m);
System.out.println(stduent2.money.m);
stduent2.money.m = 13.6;
System.out.println("Result modified by stduent2");
System.out.println(stduent1.money.m);
System.out.println(stduent2.money.m);
}
}
// Results of the
The result before modification through stduent2
99.99
99.99
The result modified by stduent2
13.6
13.6

As shown in the above code, we can see that through clone, we only copy the Student object. But the Money object in the Student object is not copied. After the value of m is modified through the reference of stduent2, the value also changes when the reference of stduent1 accesses m. The reason is that after stduent2 copies stduent1, its member variable money and stduent1’s money are at the same address. A shallow copy occurs here.
As shown in the picture:

How to avoid this situation? This requires using deep copy, the code is as follows:

class Money implements Cloneable{<!-- -->
    public double m=25.0;
    @Override
    protected Object clone() throws CloneNotSupportedException {<!-- -->
        return super.clone();
    }
}
class Student implements Cloneable {<!-- -->
    public String name;
    public int age;
    public Money money=new Money();
    @Override //Deep copy, copy the objects in the referenced object (student1) to the target object
    protected Object clone() throws CloneNotSupportedException {<!-- -->
        Student temp=(Student) super.clone();
        temp.money=(Money)this.money.clone();
        return temp;
    }
}
public class demo {<!-- -->
    public static void main(String[] args) throws CloneNotSupportedException {<!-- -->
        Student student1 = new Student();
        Student student2 = (Student) student1.clone();
        System.out.println("Result before modification through stduent2");
        System.out.println(student1.money.m);
        System.out.println(student2.money.m);
        student2.money.m = 13.6;
        System.out.println("Result modified by stduent2");
        System.out.println(student1.money.m);
        System.out.println(student2.money.m);
    }
}
//Results of the:
The result before modification through stduent2
25.0
25.0
The result modified by stduent2
25.0
13.6

The reasons are as follows:
The deep copy changes the address of student2’s money after copying, so modifying the value of m will not affect the value of student1.