Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java8 Null-safe comparison

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

like image 332
Remo Avatar asked May 07 '18 13:05

Remo


People also ask

Is compareTo null safe?

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.

IS null equals object safe?

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 .

Is null comparable?

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.


1 Answers

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.

like image 112
Mạnh Quyết Nguyễn Avatar answered Oct 28 '22 19:10

Mạnh Quyết Nguyễn