Below is my code written specifically to use custom Comparator
with max
in Java 8 Stream
.
import java.math.BigDecimal;
import java.util.*;
public class BigDecimalMax {
public static BigDecimal getBigDecimalMax(List<forTest> list) {
return list.stream()
.filter(t -> t.id % 2 == 0)
.max(forTestComparator::compare) //<-- syntax error ----------
.orElse(null);
}
public static class forTestComparator implements Comparator<forTest> {
@Override
public int compare(forTest val1, forTest val2) {
return val1.value.compareTo(val2.value);
}
}
public static void main(String[] args) {
List<forTest> lst = new ArrayList<>();
Random rn = new Random();
BigDecimalMax bdm = new BigDecimalMax();
for (int i=1; i<22; ++i) {
lst.add(bdm.new forTest(i, BigDecimal.valueOf(rn.nextLong())));
}
System.out.println(getBigDecimalMax(lst));
}
class forTest {
public int id;
public BigDecimal value;
forTest(int id, BigDecimal value) {
this.id = id;
this.value = value;
}
@Override
public String toString() {
return "forTest{" +
"id=" + id +
", value=" + value +
'}';
}
}
}
I'm getting a syntax error on a method reference which I don't understand.
Error:(15, 18) java: incompatible types: invalid method reference
cannot find symbol
symbol: method compare(BigDecimalMax.forTest, BigDecimalMax.forTest)
location: class BigDecimalMax.forTestComparator
while IntelliJ IDEA complains that Non-static method cannot be referenced from a static context
.
What exactly am I doing wrong here?
ADDITIONAL EXPLANATION (04/24/14):
I understand now the reason for the syntax error. Thank you.
Was custom Comparator
actually needed here?
Since BigDecimal
implements Comparable
but does not seem to implement Comparator
( it has CompareTo()
but no Compare()
) I thought that custom Comparator
was necessary. That's why I could not just use Comparator.comparing(ft -> ft.value)
. Is there a flaw in my logic?
Sotirios Delimanolis' answer shows how to fix the problems, but I have a few things to add.
If you already have a class that implements Comparator, you don't need to use a method reference to its compare() method. You can just pass an instance of it directly, since max() takes a reference to a Comparator:
.max(new forTestComparator())
or
forTestComparator instance = new forTestComparator();
...
.max(instance)
However, the combinator functions on Comparator usually make it unnecessary to have a class that implements Comparator. For example, you can get rid of the forTestComparator
class entirely and just do this:
.max(Comparator.comparing(ft -> ft.value))
or if forTest
were to have the obvious getValue() method, one could rewrite the stream max() call as follows:
.max(Comparator.comparing(forTest::getValue))
In addition, if you wanted to make forTest
implement the Comparable
interface, you could do this:
public class forTest implements Comparable<forTest> {
@Override
public int compareTo(forTest other) {
return this.value.compareTo(other.value);
}
...
}
and the way to use max() on a Comparable is:
.max(Comparator.naturalOrder())
Two style notes:
I strongly discourage using orElse(null)
on instances of Optional
. This is allowed, though probably its main purpose to retrofit use of new Java 8 APIs into code that's expecting null to indicate the absence of a value. Avoid orElse(null)
if you possibly can, since this forces the caller to check for null. Instead, substitute an actual value to replace an absent value, or return the Optional
itself to the caller, so the caller can apply whatever policy it wants.
I recommend sticking to the established Java naming conventions of capitalized, mixed-case class names. The class names forTest
and forTestComparator
make this code kind of difficult to work with, since they don't look like class names.
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