对象实用程序类 - Sip of Java

Objects 实用程序类随 JDK 1.7 一起推出,它提供了一个丰富的 API,用于对对象执行许多常见操作。在许多 JDK 版本中,Objects 类已经历了多次更新,其中 JDK 8 和 9 中进行了重大更新,而 JDK 16 和 19 中进行了小幅更新。我们来探索如何使用 Objects 类。

比较对象

Objects 提供了多种选项来比较两个对象的值。使用 Objects 实现的一个主要优势是它们的 null 安全性。

equals()

下面是一个比较两个记录的示例。

record RaceTime(String runnerName, Duration time) {}

RaceTime nullValue = null;
RaceTime billy = 
	new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime copyOfbilly = 
	new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime nicolai = 
	new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));
nullValue.equals(billy);//NPE
Objects.equals(nullValue, billy);// false
Objects.equals(billy, nicolai);// false
Objects.equals(billy, copyOfbilly);// true

deepEquals()

Objects 还有 deepEquals() 用于两个数组。与普通的 equals() 不同,这将比较存储在数组中的值,这应该会产生更一致的结果。从根本上讲,此方法会传递到 Arrays.deepEquals()

record RaceTime(String runnerName, Duration time) {}
RaceTime billy = 
	new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS));
RaceTime nicolai = 
	new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));

RaceTime[] raceTimes1 = new RaceTime[] { billy, nicolai };
RaceTime[] raceTimes2 = new RaceTime[] { billy, nicolai };
Objects.equals(raceTimes1, raceTimes2);// false
Objects.deepEquals(raceTimes1, raceTimes2);// true

compare()

Objects 还具有 compare(),它可以接受两个对象和一个 Comparator<T>compare()Objects 中少数几个非 null 安全的方法之一,因为如果其中一个参数为 null,则没有合理的返回值。

record RaceTime(String runnerName, Duration time) {}
class RaceTimeComparator implements Comparator<RaceTime> {
@Override
public int compare(RaceTime o1, RaceTime o2) {
	return o1.time.compareTo(o2.time);
}
}
RaceTime billy = 
new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS)); 
RaceTime nicolai = 
new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));
Objects.compare(billy, nicolai, new RaceTimeComparator());//-1
Objects.compare(null, nicolai, new RaceTimeComparator());//NPE

字符串和哈希码

Objects 还提供了将对象转换为其 String 和哈希码值的方法。同样,这些方法的主要优点是它们的 null 安全性。

转换为字符串

更有趣的方法之一是 toString(obj, nullDefault),它在 null 的情况下提供一个默认值。 toIdentityString(obj) 返回传递对象的 toString()hashCode(),就好像这两个方法都没有被覆盖一样。

record RaceTime(String runnerName, Duration time) {}

RaceTime nullValue = null;
RaceTime billy = 
	new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS)); 
RaceTime nicolai = 
	new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));

Objects.toString(billy);//RaceTime[runnerName=Billy Korando, time=PT1M30S]
Objects.toString(nullValue);//null
Objects.toString(nullValue, "Did not finish");//Did not finish
Objects.toIdentityString(billy);//ObjectsExamples$1RaceTime@251a69d7

转换为哈希码

Objects 还提供了将对象转换为其哈希码值的方法。

record RaceTime(String runnerName, Duration time) {}

RaceTime nullValue = null;
RaceTime billy = 
	new RaceTime("Billy Korando", Duration.of(90, ChronoUnit.SECONDS)); 
RaceTime nicolai = 
	new RaceTime("Nicolai Parlog", Duration.of(180, ChronoUnit.SECONDS));

Objects.hashCode(nullValue);//0
Objects.hashCode(billy);//[HashValue]
Objects.hash(billy, nicolai);//[HashValue]

空值检查

Objects 提供了许多检查和处理 null 的方法。

对 null 值抛出 NullPointException

requireNonNull(obj) 如果传递的值为 null,则会抛出 NullPointException

record RaceTime(String runnerName, Duration time) {
	RaceTime{
		runnerName = Objects.requireNonNull(runnerName);	
		time = Objects.requireNonNull(time);
	}
}

对 null 值抛出 NullPointException 并附带自定义消息

requireNonNull(obj, String) 如果传递的值为 null,则会抛出带有自定义消息的 NullPointException

record RaceTime(String runnerName, Duration time) {
	RaceTime{
		runnerName = Objects.requireNonNull(runnerName, "runner name required!");
		time = Objects.requireNonNull(time, "race time required!");
	}
}

对 null 值返回默认值

requireNonNullElse(obj, defaultValue) 如果 objnull,将返回传递的 defaultValue

record RaceTime(String runnerName, Duration time) {
	RaceTime{
		runnerName = Objects.requireNonNullElse(runnerName, "John Smith");
		time = Objects.requireNonNullElse(time, Duration.ZERO);
	}
}

使用提供程序

Objects 还提供方法 requireNonNull(obj, Supplier<String>)T requireNonNullElseGet(T, Supplier<T>),它们可以使用 Supplier 提供消息或默认值。仅当生成消息或默认值可能对性能产生重大影响时,才应使用这些方法。

record RaceTime(String runnerName, Duration time) {
static Supplier<String> noNameMsgSupplier = () -> "runner name required!";
static Supplier<String> noTimeMsgSupplier = () -> "race time required!";
	RaceTime{
		runnerName = Objects.requireNonNull(runnerName, noNameMsgSupplier);
		time = Objects.requireNonNull(time, noTimeMsgSupplier);
	}
}
record RaceTime(String runnerName, Duration time) {
static Supplier<String> noNameValueSupplier = () -> "John Smith";
static Supplier<Duration> noTimeValueSupplier = () -> Duration.ZERO;
	RaceTime{
		runnerName = Objects.requireNonNullElseGet(runnerName, noNameValueSupplier);
		time = Objects.requireNonNullElseGet(time, noTimeValueSupplier);
	}
}

谓词空检查

Objects 还提供用于谓词的空检查,尽管它也可以用于其他场景。

record RaceTime(String runnerName, Duration time) {}

RaceTime nullValue = null;
Objects.nonNull(nullValue);//false
Objects.isNull(nullValue);//true

索引检查

Objects 还提供多种选项,用于在遍历 FileStringCollection 或类似对象时检查索引位置。其中一些方法最近在 JDK 16 中添加。

int checkIndex(int index, int length) 

int checkFromToIndex(int fromIndex, int toIndex, int length)

int checkFromIndexSize(int fromIndex, int size, int length)

long checkIndex(long index, long length)

long checkFromToIndex(long fromIndex, long toIndex, long length)

long checkFromIndexSize(long fromIndex, long size, long length)

延伸阅读

Objects JDK 20 Javadoc

编码愉快!