有人可以解释为什么我在以下代码中将lambda表达式放入Copmarator.compring()时不起作用。当我将相同的表达式提取到Function变量中时,它可以正常工作。
有两个示例(在Main.java类中),其中一个未编译(一个带有Optional),另一个正在编译,但在执行时抛出错误。我在注释中添加了无效代码,以表明将表达式提取到变量中后可以正常工作。
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);
}
}
更新:
我正在为希望将其分开的那些人分隔此更新中的代码。
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);
}
}
答案 0 :(得分:1)
将变量键入为Function<Book, Integer>
时,编译器将知道确切的泛型类型。内联版本中缺少此信息。指定显式类型参数有助于如下:
.sorted(
Comparator.<Book, Integer> comparing(....
另请参阅this答案。
答案 1 :(得分:1)
问题在于泛型类型的解析。我不确定,但是我认为这是thenComparing
的签名,期望Function<? super T, ...>
。这意味着“根”比较器必须绑定到sth。 “超级” T
,即尽可能广泛的类型,即Object
。
明确定义泛型类型,也将使其编译(请参见Michal的anwser):
.sorted(Comparator.<Book, Integer> comparing(b -> b.getPrice())
.thenComparing(...))
答案 2 :(得分:0)
IntellIj告诉b
是Object
,而不是Book
:
要使其正常运行,请将b
强制转换为Book
:
books.stream()
.filter(b -> b == null || b.getDate().getYear() > 1990)
.sorted(comparing((Book 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);
第二部分相同。