Having issues with comparing two products. I wish to compare the vintage (which is optional) attribute of each of them. But whenever this attribute is null, a NPE is thrown. I thought with Comparator.nullsLast(..) I can deal with null values... But it seems I either have a misunderstanding of how this works or there's something wrong with the code. What do I need to change to get this work null-friendly?
@Override
public int compare(IProduct product1, IProduct product2) throws ProductComparisonException {
Comparator<IShopProduct> comparator =
Comparator.nullsLast(Comparator.comparing(IShopProduct::getVintage));
return comparator.compare((IShopProduct)product1.getProvidedProductData(),
(IShopProduct)product2.getProvidedProductData());
}
Thanks in advance
The compare() method in StringUtils class is a null-safe version of the compareTo() method of String class and handles null values by considering a null value less than a non-null value. Two null values are considered equal.
The expression obj. equals(null) will always be false. The program uses the equals() method to compare an object with null . This comparison will always return false, since the object is not null .
That is, a comparison against a null value does not evaluate to true or false , it evaluates to unknown . Often this is an implementation of ANSI nulls, but this is not always the case. By default in SQL Server, the null-equals-null comparison returns a null value.
It should be
Comparator<IShopProduct> comparator =
Comparator.comparing( IShopProduct::getVintage,
Comparator.nullLast(naturalOrder()));
Comparator.nullFirst()/nullLast()
consider null value being greater/smaller than nonNull object
Edit
This is implementation of Comparator.comparing():
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
As you can see it calls keyExtractor.apply(c1).compareTo()
so it will throw NPE if keyExtractor.apply(c1)
is null
My suggested code using the following function:
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
}
Basically it will extracts the value, then pass the comparing values to Comparator
.
The values will be passed to the naturalOrder()
comparator which resolved to value1.compareTo(value2)
. Normally it will throw NPE but we have wrapped it with Comparator.nullLast
which have special null
handler.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With