Can someone explain why when I put lambda expression into Copmarator.compring() in the following code, it's not working. And when I extract the same expression into Function variables, it works fine.
There is two examples (in the Main.java class), one of them is not compiling (The one with Optional) and the other is compiling but throw an error when executed. I've put in comment the not working code to show that it works fine when the expressions are extracted into variables.
package com.slide;
import java.time.LocalDate;
import java.time.Month;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import static java.util.Comparator.*;
import static java.util.Comparator.reverseOrder;
public class Main {
static class Book {
private int price;
private LocalDate date;
private Author author;
public Book(int price, LocalDate date, Author author) {
this.price = price;
this.date = date;
this.author = author;
}
public int getPrice() { return price; }
public void setPrice(int price) { this.price = price; }
public LocalDate getDate() { return date; }
public void setDate(LocalDate date) { this.date = date; }
public Author getAuthor() { return author; }
public void setAuthor(Author author) { this.author = author; }
@Override
public String toString() {
String authorName = author != null && author.getName() != null ? author.getName() : "null";
return "{ price : "
.concat(String.valueOf(price))
.concat(", year : ")
.concat(date != null ? date.toString() : "null")
.concat(", author : ")
.concat(authorName)
.concat("}");
}
}
static class Author {
private String name;
public Author(String name) {
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
static Author john = new Author("John");
static Author marguerite = new Author("Marguerite");
static Author charlton = new Author("Charlton");
static Author gwen = new Author("Gwen");
static Author marion = new Author("Marion");
static Author mark = new Author("Mark");
static Author jerry = new Author("Jerry");
static Author bob = new Author("Bob");
static Author dennis = new Author("Dennis");
static Author mike = new Author(null);
static Book b1 = new Book(15, LocalDate.of(1983, Month.JANUARY, 1), john);
static Book b2 = new Book(8, LocalDate.of(1998, Month.AUGUST, 1), marguerite);
static Book b3 = new Book(14, LocalDate.of(1992, Month.SEPTEMBER, 1), john);
static Book b4 = new Book(10, LocalDate.of(2016, Month.MAY, 1), charlton);
static Book b5 = new Book(10, LocalDate.of(1997, Month.FEBRUARY, 1), gwen);
static Book b6 = new Book(8, LocalDate.of(1986, Month.AUGUST, 1), charlton);
static Book b7 = new Book(54, LocalDate.of(1998, Month.MARCH, 1), john);
static Book b8 = new Book(10, LocalDate.of(1997, Month.FEBRUARY, 1), marion);
static Book b9 = new Book(32, LocalDate.of(2013, Month.JULY, 1), mark);
static Book b10 = new Book(5, LocalDate.of(1997, Month.AUGUST, 1), jerry);
static Book b11 = new Book(10, LocalDate.of(2008, Month.MAY, 1), bob);
static Book b12 = new Book(9, LocalDate.of(1978, Month.FEBRUARY, 1), dennis);
static Book b13 = new Book(10, LocalDate.of(1995, Month.FEBRUARY, 1), mike);
static Book b14 = new Book(10, LocalDate.of(1995, Month.FEBRUARY, 1),null);
public static List<Book> givenBooksWithNulls(){
return Arrays.asList(b1, b2, b3, b4, null, b5, b6, b7, b8, null, b13, b9, b10, b14, b11, b12);
}
public static void main(String[] args) {
List<Book> books = givenBooksWithNulls();
Function<Book, Integer> fPrice = b -> b == null ? null : b.getPrice();
Function<Book, String> fAuthor = b -> b == null ? null : b.getAuthor() == null ? null : b.getAuthor().getName();
// Woks good with the Functions as Variables
books.stream()
.filter(b -> b == null || b.getDate().getYear() > 1990)
.sorted(comparing(fPrice, nullsLast(naturalOrder()))
.thenComparing(fAuthor, nullsLast(reverseOrder()))
)
.forEach(System.out::println);
// It doesn't work when I put the content of the Functions inline
// Java says getPrice() cannot be find in java.lang.Object
// it seems like comparing doesn't like the Lambda expression
// books.stream()
// .filter(b -> b == null || b.getDate().getYear() > 1990)
// .sorted(comparing(b -> b == null ? null : b.getPrice(), nullsLast(naturalOrder()))
// .thenComparing(b -> b == null ? null : b.getAuthor() == null ? null : b.getAuthor().getName(), nullsLast(reverseOrder()))
// )
// .forEach(System.out::println);
System.out.println("====================================");
// Other example using Optional
Predicate<Book> fFilter = b -> Optional.ofNullable(b)
.map(bb -> bb.getDate().getYear() > 1990)
.orElse(false);
Function<Book, Integer> fPrice2 = b -> Optional.ofNullable(b)
.map(Book::getPrice)
.orElse(null);
Function<Book, String> fAuthor2 = b -> Optional.ofNullable(b)
.map(Book::getAuthor)
.map(Author::getName)
.orElse(null);
// This also works when I use the Predicate and Functions as variables
givenBooksWithNulls().stream()
.filter(fFilter)
.sorted(comparing(fPrice2, nullsLast(naturalOrder()))
.thenComparing(fAuthor2, nullsLast(reverseOrder()))
)
.forEach(System.out::println);
// Same thing Here, not working when we replace the variables by their content of Lambda expression
// givenBooksWithNulls().stream()
// .filter(b -> Optional.ofNullable(b)
// .map(bb -> bb.getDate().getYear() > 1990)
// .orElse(false))
// .sorted(comparing(b -> Optional.ofNullable(b)
// .map(Book::getPrice)
// .orElse(null), nullsLast(naturalOrder()))
// .thenComparing(b -> Optional.ofNullable(b)
// .map(Book::getAuthor)
// .map(Author::getName)
// .orElse(null), nullsLast(reverseOrder()))
// )
// .forEach(System.out::println);
}
}
UPDATE :
I'm separating the code in this update for those who prefer it separated.
Author.java
package com.slide;
public class Author {
private String name;
public Author(String name) {
this.name = name;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
}
Book.java
package com.slide;
import java.time.LocalDate;
public class Book {
private int price;
private LocalDate date;
private Author author;
public Book(int price, LocalDate date, Author author) {
this.price = price;
this.date = date;
this.author = author;
}
public int getPrice() { return price; }
public void setPrice(int price) { this.price = price; }
public LocalDate getDate() { return date; }
public void setDate(LocalDate date) { this.date = date; }
public Author getAuthor() { return author; }
public void setAuthor(Author author) { this.author = author; }
@Override
public String toString() {
String authorName = author != null && author.getName() != null ? author.getName() : "null";
return "{ price : "
.concat(String.valueOf(price))
.concat(", year : ")
.concat(date != null ? date.toString() : "null")
.concat(", author : ")
.concat(authorName)
.concat("}");
}
}
DataExample.java
package com.slide;
import java.time.LocalDate;
import java.time.Month;
import java.util.Arrays;
import java.util.List;
public class DataExample {
static Author john = new Author("John");
static Author marguerite = new Author("Marguerite");
static Author charlton = new Author("Charlton");
static Author gwen = new Author("Gwen");
static Author marion = new Author("Marion");
static Author mark = new Author("Mark");
static Author jerry = new Author("Jerry");
static Author bob = new Author("Bob");
static Author dennis = new Author("Dennis");
static Author mike = new Author(null);
static Book b1 = new Book(15, LocalDate.of(1983, Month.JANUARY, 1), john);
static Book b2 = new Book(8, LocalDate.of(1998, Month.AUGUST, 1), marguerite);
static Book b3 = new Book(14, LocalDate.of(1992, Month.SEPTEMBER, 1), john);
static Book b4 = new Book(10, LocalDate.of(2016, Month.MAY, 1), charlton);
static Book b5 = new Book(10, LocalDate.of(1997, Month.FEBRUARY, 1), gwen);
static Book b6 = new Book(8, LocalDate.of(1986, Month.AUGUST, 1), charlton);
static Book b7 = new Book(54, LocalDate.of(1998, Month.MARCH, 1), john);
static Book b8 = new Book(10, LocalDate.of(1997, Month.FEBRUARY, 1), marion);
static Book b9 = new Book(32, LocalDate.of(2013, Month.JULY, 1), mark);
static Book b10 = new Book(5, LocalDate.of(1997, Month.AUGUST, 1), jerry);
static Book b11 = new Book(10, LocalDate.of(2008, Month.MAY, 1), bob);
static Book b12 = new Book(9, LocalDate.of(1978, Month.FEBRUARY, 1), dennis);
static Book b13 = new Book(10, LocalDate.of(1995, Month.FEBRUARY, 1), mike);
static Book b14 = new Book(10, LocalDate.of(1995, Month.FEBRUARY, 1),null);
public static List<Book> givenBooksWithNulls(){
return Arrays.asList(b1, b2, b3, b4, null, b5, b6, b7, b8, null, b13, b9, b10, b14, b11, b12);
}
}
Main.java
package com.slide;
import java.util.List;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Predicate;
import static java.util.Comparator.*;
import static java.util.Comparator.reverseOrder;
public class Main {
public static void main(String[] args) {
List<Book> books = DataExample.givenBooksWithNulls();
exampleOne(books);
System.out.println("====================================");
exampleTwo(books);
}
private static void exampleOne(List<Book> books) {
Function<Book, Integer> fPrice = b -> b == null ? null : b.getPrice();
Function<Book, String> fAuthor = b -> b == null ? null : b.getAuthor() == null ? null : b.getAuthor().getName();
// Woks good with the Functions as Variables
books.stream()
.filter(b -> b == null || b.getDate().getYear() > 1990)
.sorted(comparing(fPrice, nullsLast(naturalOrder()))
.thenComparing(fAuthor, nullsLast(reverseOrder()))
)
.forEach(System.out::println);
// It doesn't work when I put the content of the Functions inline
// Java says getPrice() cannot be find in java.lang.Object
// it seems like comparing doesn't like the Lambda expression
// books.stream()
// .filter(b -> b == null || b.getDate().getYear() > 1990)
// .sorted(comparing(b -> b == null ? null : b.getPrice(), nullsLast(naturalOrder()))
// .thenComparing(b -> b == null ? null : b.getAuthor() == null ? null : b.getAuthor().getName(), nullsLast(reverseOrder()))
// )
// .forEach(System.out::println);
}
private static void exampleTwo(List<Book> books) {
// Other example using Optional
Predicate<Book> fFilter = b -> Optional.ofNullable(b)
.map(bb -> bb.getDate().getYear() > 1990)
.orElse(false);
Function<Book, Integer> fPrice2 = b -> Optional.ofNullable(b)
.map(Book::getPrice)
.orElse(null);
Function<Book, String> fAuthor2 = b -> Optional.ofNullable(b)
.map(Book::getAuthor)
.map(Author::getName)
.orElse(null);
// This also works when I use the Predicate and Functions as variables
books.stream()
.filter(fFilter)
.sorted(comparing(fPrice2, nullsLast(naturalOrder()))
.thenComparing(fAuthor2, nullsLast(reverseOrder()))
)
.forEach(System.out::println);
// Same thing Here, not working when we replace the variables by their content of Lambda expression
// books.stream()
// .filter(b -> Optional.ofNullable(b)
// .map(bb -> bb.getDate().getYear() > 1990)
// .orElse(false))
// .sorted(comparing(b -> Optional.ofNullable(b)
// .map(Book::getPrice)
// .orElse(null), nullsLast(naturalOrder()))
// .thenComparing(b -> Optional.ofNullable(b)
// .map(Book::getAuthor)
// .map(Author::getName)
// .orElse(null), nullsLast(reverseOrder()))
// )
// .forEach(System.out::println);
}
}