bad*_*der 16 java java-8 functional-interface
这段代码有什么区别?
Supplier<LocalDate> s1 = LocalDate::now;
LocalDate s2 = LocalDate.now();
System.out.println(s1.get()); //2016-10-25
System.out.println(s2); //2016-10-25
Run Code Online (Sandbox Code Playgroud)
我开始学习Java 8中的功能接口,而不了解供应商的好处.究竟何时以及如何使用它们.供应商是否提高了绩效或者提取抽象水平的好处?
谢谢你的回答!这不是重复的问题,因为我使用搜索并没有找到我需要的东西.
更新1: 你的意思是这个?
Supplier<Long> s1 = System::currentTimeMillis;
Long s2 = System.currentTimeMillis();
System.out.println(s1.get()); //1477411877817
System.out.println(s2); //1477411877817
try {
Thread.sleep(3000l);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(s1.get()); //1477411880817 - different
System.out.println(s2); //1477411877817
Run Code Online (Sandbox Code Playgroud)
Bál*_*int 13
它绝对不会改善性能.您的问题与此类似:我们为什么要使用变量?我们可以简单地在每次需要时重新计算所有内容.对?
如果你需要多次使用一个方法,但它有一个罗嗦的语法.
假设您有一个名为的类MyAmazingClass,并且您在其中有一个名称MyEvenBetterMethod(这是静态的)方法,您需要在代码中的15个不同位置调用它15次.当然,你可以做点什么......
int myVar = MyAmazingClass.MyEvenBetterMethod();
// ...
int myOtherVar = MyAmazingClass.MyEvenBetterMethod();
// And so on...
Run Code Online (Sandbox Code Playgroud)
......但你也可以这样做
Supplier<MyAmazingClass> shorter = MyAmazingClass::MyEvenBetterMethod;
int myVar = shorter.get();
// ...
int myOtherVar = shorter.get();
// And so on...
Run Code Online (Sandbox Code Playgroud)
jos*_*den 12
我会经历一个我们应该使用的场景Supplier<LocalDate>而不是LocalDate.
直接调用静态方法的代码LocalDate.now()很难进行单元测试.考虑我们想要getAge()计算一个计算一个人年龄的方法的单元测试的场景:
class Person {
final String name;
private final LocalDate dateOfBirth;
Person(String name, LocalDate dateOfBirth) {
this.name = name;
this.dateOfBirth = dateOfBirth;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, LocalDate.now());
}
}
Run Code Online (Sandbox Code Playgroud)
这在生产中很好用.但是单元测试要么必须将系统的日期设置为已知值,要么每年更新以期望返回的年龄增加1,两者都是非常令人难以置信的解决方案.
更好的解决方案是单元测试在已知日期注入,同时仍允许生产代码使用LocalDate.now().也许是这样的:
class Person {
final String name;
private final LocalDate dateOfBirth;
private final LocalDate currentDate;
// Used by regular production code
Person(String name, LocalDate dateOfBirth) {
this(name, dateOfBirth, LocalDate.now());
}
// Visible for test
Person(String name, LocalDate dateOfBirth, LocalDate currentDate) {
this.name = name;
this.dateOfBirth = dateOfBirth;
this.currentDate = currentDate;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, currentDate);
}
}
Run Code Online (Sandbox Code Playgroud)
考虑自创建对象以来该人的生日已过去的场景.通过此实现,getAge()将基于何时创建Person对象而不是当前日期.我们可以通过使用Supplier<LocalDate>:
class Person {
final String name;
private final LocalDate dateOfBirth;
private final Supplier<LocalDate> currentDate;
// Used by regular production code
Person(String name, LocalDate dateOfBirth) {
this(name, dateOfBirth, ()-> LocalDate.now());
}
// Visible for test
Person(String name, LocalDate dateOfBirth, Supplier<LocalDate> currentDate) {
this.name = name;
this.dateOfBirth = dateOfBirth;
this.currentDate = currentDate;
}
long getAge() {
return ChronoUnit.YEARS.between(dateOfBirth, currentDate.get());
}
public static void main(String... args) throws InterruptedException {
// current date 2016-02-11
Person person = new Person("John Doe", LocalDate.parse("2010-02-12"));
printAge(person);
TimeUnit.DAYS.sleep(1);
printAge(person);
}
private static void printAge(Person person) {
System.out.println(person.name + " is " + person.getAge());
}
}
Run Code Online (Sandbox Code Playgroud)
输出正确是:
John Doe is 5
John Doe is 6
Run Code Online (Sandbox Code Playgroud)
我们的单元测试可以像这样注入"now"日期:
@Test
void testGetAge() {
Supplier<LocalDate> injectedNow = ()-> LocalDate.parse("2016-12-01");
Person person = new Person("John Doe", LocalDate.parse("2004-12-01"), injectedNow);
assertEquals(12, person.getAge());
}
Run Code Online (Sandbox Code Playgroud)
您正在混淆功能接口和方法引用.Supplier只是一个接口,类似于Callable,自Java 5以来你应该知道,唯一的区别Callable.call是允许抛出被检查的Exceptions,不像Supplier.get.所以这些接口将具有类似的用例.
现在,这些接口也恰好是功能接口,这意味着它们可以作为方法引用实现,指向将在调用接口方法时调用的现有方法.
所以在Java 8之前,你必须写
Future<Double> f=executorService.submit(new Callable<Double>() {
public Double call() throws Exception {
return calculatePI();
}
});
/* do some other work */
Double result=f.get();
Run Code Online (Sandbox Code Playgroud)
现在,你可以写
Future<Double> f=executorService.submit(() -> calculatePI());
/* do some other work */
Double result=f.get();
Run Code Online (Sandbox Code Playgroud)
要么
Future<Double> f=executorService.submit(MyClass::calculatePI);
/* do some other work */
Double result=f.get();
Run Code Online (Sandbox Code Playgroud)
何时使用 的问题Callable根本没有改变.
同样,何时使用的问题Supplier不取决于您如何实现它,而是取决于您使用的API,即
CompletableFuture<Double> f=CompletableFuture.supplyAsync(MyClass::calculatePI);
/* do some other work */
Double result=f.join();// unlike Future.get, no checked exception to handle...
Run Code Online (Sandbox Code Playgroud)
我将添加我的观点,因为我对答案不满意:当您想延迟执行时,供应商很有用。
无供应商
config.getLocalValue(getFromInternet() /*value if absent*/);
Run Code Online (Sandbox Code Playgroud)
在调用 getLocalValue 之前,将调用 getFromInternet,但仅当本地值不存在时才会使用 getFromInternet() 的值。
现在,如果config.getLocalValue可以接受供应商,我们可以延迟此执行,而且如果存在本地值,我们将不会执行。
config.getLocalValue(() -> getFromInternet())
Run Code Online (Sandbox Code Playgroud)
差异
供应商使之成为可能:execute only when and if needed
供应商是否提高了性能或抽象级别的好处?
不,这不是为了提高性能。该Supplier用于即您指定的功能(代码)何时使用,将运行一个延迟执行。以下示例演示了差异:
import java.time.LocalDateTime;
import java.util.function.Supplier;
public class Main {
public static void main(String[] args) throws InterruptedException {
// Create a reference to the current date-time object when the following line is
// executed
LocalDateTime ldt = LocalDateTime.now();
System.out.println(ldt);// Line-1
// Create a reference to a functionality that will get the current date-time
// whenever this functionality will be used
Supplier<LocalDateTime> dateSupplier = LocalDateTime::now;
// Sleep for 5 seconds
Thread.sleep(5000);
System.out.println(ldt);// Will display the same value as Line-1
System.out.println(dateSupplier.get());// Will display the current date-time when this line will be executed
// Sleep again for 5 seconds
Thread.sleep(5000);
System.out.println(ldt);// Will display the same value as Line-1
System.out.println(dateSupplier.get());// Will display the current date-time when this line will be executed
}
}
Run Code Online (Sandbox Code Playgroud)
示例运行的输出:
2021-04-11T00:04:06.205105
2021-04-11T00:04:06.205105
2021-04-11T00:04:11.211031
2021-04-11T00:04:06.205105
2021-04-11T00:04:16.211659
Run Code Online (Sandbox Code Playgroud)
另一个有用的案例:
import java.util.List;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<String> list = List.of("Hello", "B2C", "World", "Stack Overflow", "is", "a", "gr8", "platform");
// A simple Stream for demo; you can think of a complex Stream with more
// intermediate operations
Stream<String> stream = list.stream()
.filter(s -> s.length() <= 5)
.map(s -> s.substring(1));
System.out.println(stream.anyMatch(s -> Character.isLetter(s.charAt(0))));
System.out.println(stream.anyMatch(s -> Character.isDigit(s.charAt(0))));
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
true
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
at java.base/java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:528)
at Main.main(Main.java:13)
Run Code Online (Sandbox Code Playgroud)
输出是不言自明的。一个丑陋的解决方法可能是Stream每次创建一个新的,如下所示:
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = List.of("Hello", "B2C", "World", "Stack Overflow", "is", "a", "gr8", "platform");
System.out.println(list.stream().filter(s -> s.length() <= 5).map(s -> s.substring(1))
.anyMatch(s -> Character.isLetter(s.charAt(0))));
System.out.println(list.stream().filter(s -> s.length() <= 5).map(s -> s.substring(1))
.anyMatch(s -> Character.isDigit(s.charAt(0))));
}
}
Run Code Online (Sandbox Code Playgroud)
现在,看看你可以如何干净地使用一个Supplier:
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
List<String> list = List.of("Hello", "B2C", "World", "Stack Overflow", "is", "a", "gr8", "platform");
Supplier<Stream<String>> streamSupplier = () -> list.stream()
.filter(s -> s.length() <= 5)
.map(s -> s.substring(1));
System.out.println(streamSupplier.get().anyMatch(s -> Character.isLetter(s.charAt(0))));
System.out.println(streamSupplier.get().anyMatch(s -> Character.isDigit(s.charAt(0))));
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
true
true
Run Code Online (Sandbox Code Playgroud)