在Programming时,经常遇到需要操纵一对参数的场景。像C++就有的std::pair。
import org.jetbrains.annotations.Contract; //可选,因为是辅助人编码用的注解
import org.jetbrains.annotations.NotNull; //可选,因为是辅助人编码用的注解
import org.jetbrains.annotations.Nullable; //可选,因为是辅助人编码用的注解
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serial;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
/**
* Generic pair.<br>
* Although the instances of this class are immutable, it is impossible
* to ensure that the references passed to the constructor will not be
* modified by the caller.
* @param Yin
* @param Yang
* @param <A> It is best to have implemented the {@code Serializable} interface, and even if it's not, the {@code Cloneable} interface must have been implemented.
* @param <B> It is best to have implemented the {@code Serializable} interface, and even if it's not, the {@code Cloneable} interface must have been implemented.
* @author DIA
*/
public record Pair<A, B>(@NotNull A Yin, @NotNull B Yang) implements Serializable, Cloneable {
@Serial private static final long serialVersionUID = -8245392630373082964L;
/**
* @return {@code Pair<A, B>}: {@code (Yin, Yang)}
*/
@Override
@NotNull
public String toString() {
return "Pair<" + Yin.getClass() + ", " + Yang.getClass() + ">: (" + Yin + ", " + Yang + ")";
}
/**
* @param otherObject the reference object with which to compare.
* @return {@code true} if the given object is also a map entry and
* the two entries represent the same mapping.
*/
@Override
@Contract(value = "null -> false", pure = true)
public boolean equals(@Nullable final Object otherObject) {
return (this == otherObject) ||
((otherObject instanceof Pair<?,?> pair) &&
Objects.equals(Yin, pair.Yin) && Objects.equals(Yang, pair.Yang));
}
/**
* @param otherYin Type A parameter of other record {@code Pair<A, B>}
* @param otherYang Type B parameter of other record {@code Pair<A, B>}
* @return {@code true} if {@code otherYin} equals to {@code Yin} and {@code otherYang} equals to {@code Yang}
*/
public boolean equals(@Nullable final A otherYin, @Nullable final B otherYang) {
return Yin.equals(otherYin) && Yang.equals(otherYang);
}
/**
* @return a {@code Pair<A, B>} instance obtained by <strong>deep cloning</strong>
* if A and B both have implemented {@code Serializable} or {@code Cloneable} interface.
* Otherwise, the instance {@code Pair<A, B>} obtained by the <strong>shallow copy</strong> will be returned.
* @throws CloneNotSupportedException {@code throw new CloneNotSupportedException} if Both not be implemented {@code Serializable}.
*/
@SuppressWarnings("unchecked")
@Override
@Contract(" -> new")
@NotNull
public Pair<A, B> clone() throws CloneNotSupportedException {
if ( Yin instanceof Serializable && Yang instanceof Serializable ) {
try(final var BYTES_OUT = new ByteArrayOutputStream();
final var OOS = new ObjectOutputStream(BYTES_OUT)
) {
OOS.writeUnshared(Yin);
OOS.writeUnshared(Yang);
try(final var OIS = new ObjectInputStream(new ByteArrayInputStream(BYTES_OUT.toByteArray())
) {
return new Pair<>((A) OIS.readUnshared(), (B) OIS.readUnshared()); //必定返回深克隆的对象
}
} catch (final IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}else if ( Yin instanceof Cloneable && Yang instanceof Cloneable ) {
try {
return new Pair<>((A) Yin.getClass().getDeclaredMethod("clone").invoke(Yin), (B) Yang.getClass().getDeclaredMethod("clone").invoke(Yang));
} catch (final IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
}
return (Pair<A, B>) super.clone(); //有可能会返回浅克隆的对象
}
}
主要涉及到的知识点为record基础类型的coding,Java的反射,以及Deep Cloning深拷贝的具体实现,并且我让.clone()方法无论如何都要返回一个cloned实例对象(有可能是浅拷贝的),而且尽可能地是深拷贝的实例对象。
Java record基础类型 多种方式实现深拷贝,主要用过序列化与反序列化实现绝对的深拷贝。同时也采用了反射来进行深拷贝,因为Object的.clone()方法是protected级别的。即使深拷贝失败,也至少会返回浅拷贝的实例对象。
下面是简单的测试用例:
@Test
void test03() throws CloneNotSupportedException {
final var array = new ArrayList<Integer>();
final var p0 = new Pair<>(array, "3225");
final var p1 = new Pair<>(new ArrayList<>(List.of(56455, 425, 85638, 9)), "3225");
array.add(56455);
array.add(425);
array.add(85638);
final var p2 = p0.clone();
p1.Yin().remove(p1.Yin().size() - 1);
Assertions.assertEquals(p0, p1);
Assertions.assertEquals(p0, p2);
System.out.println(p0.hashCode());
array.clear();
System.out.println(p0);
System.out.println(p1);
System.out.println(p2);
System.out.println(p1.hashCode() + "------" + p2.hashCode());
Assertions.assertNotEquals(p0, p2);
Assertions.assertEquals(p1, p2);
}
该测试成功通过✅,运作效果完全在计划之中,尚未发现任何偏差。
唯一的遗憾便是SonarLint老是对我的.clone()方法发出警告,说啥必须要使用构造方法、工厂方法构造的方式,来代替.clone()方法。