2016-10-25 13 views
5

Jaka jest różnica między tym kodem?Kiedy powinniśmy używać dostawcy w Java 8?

Supplier<LocalDate> s1 = LocalDate::now; 
LocalDate s2 = LocalDate.now(); 

System.out.println(s1.get()); //2016-10-25 
System.out.println(s2); //2016-10-25 

Zacząłem uczyć się interfejsów funkcjonalnych w Javie 8 i nie rozumiem, jakie korzyści zapewnia Dostawcy. Kiedy i jak, dokładnie, powinniśmy go użyć. Czy dostawca poprawia wydajność, a może korzyści na poziomie abstrakcji?

Dzięki za odpowiedzi! I nie jest to duplikat pytania, ponieważ użyłem wyszukiwarki i nie znalazłem tego, czego potrzebuję.

UPDATE 1: Masz na myśli to?

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 
+2

's2' da ci "teraz" w momencie, w którym 's2' został przypisany. 's1.get()' da ci "teraz" w momencie, gdy wywołasz 'get()'. To może nie być to samo, jeśli przekazałeś 'Dostawcę 'gdzieś do użycia, gdy jest to wymagane. Następnie, jeśli ponownie nazwiesz 's1.get()' ponownie, dostaniesz inny czas ponownie. – khelwood

+0

@khelwood, nie dostałem tego i zaktualizowałem pytanie, proszę sprawdzić. – badCoder

+1

Użyj go, gdy musisz przekazać _wielkie wartości_ w _inną metodę._ To prawie 100% ważnych przypadków użycia. –

Odpowiedz

2

To zdecydowanie nie poprawia wydajności. Twoje pytanie jest podobne do tego: Dlaczego używamy zmiennych? Możemy po prostu przeliczać wszystko za każdym razem, kiedy tego potrzebujemy. Dobrze?

Jeśli potrzebujesz użyć metody wiele razy, ale ma ona gorszą składnię.

Załóżmy, że masz klasę o nazwie MyAmazingClass i masz w niej metodę o nazwie MyEvenBetterMethod (która jest statyczna) i musisz wywołać ją 15 razy na 15 różnych pozycjach w kodzie. Oczywiście, można zrobić coś takiego ...

int myVar = MyAmazingClass.MyEvenBetterMethod(); 
// ... 
int myOtherVar = MyAmazingClass.MyEvenBetterMethod(); 
// And so on... 

... ale można też zrobić

Supplier<MyAmazingClass> shorter = MyAmazingClass::MyEvenBetterMethod; 

int myVar = shorter.get(); 
// ... 
int myOtherVar = shorter.get(); 
// And so on... 
+4

Z pewnością byłby to 'Dostawca '. I nie ma to nic wspólnego z wydajnością ani z rozwagą jakąś metodą. Jest różnica semantyczna. – biziclop

7

Mylisz funkcjonalne interfejsy i referencje metod. Supplier to tylko interfejs, podobny do Callable, który powinieneś znać od czasu Java 5, jedyną różnicą jest to, że Callable.call może rzutować sprawdzone Exception s, w przeciwieństwie do Supplier.get. Więc te interfejsy będą miały podobne zastosowania.

Interfejs funkcjonalny to obecnie funkcjonalny interfejs, co oznacza, że ​​mogą być implementowane jako odniesienie do metody, wskazując na istniejącą metodę, która zostanie wywołana po wywołaniu metody interfejsu.

Więc zanim Java 8, trzeba było napisać

Future<Double> f=executorService.submit(new Callable<Double>() { 
    public Double call() throws Exception { 
     return calculatePI(); 
    } 
}); 
/* do some other work */ 
Double result=f.get(); 

i teraz można napisać

Future<Double> f=executorService.submit(() -> calculatePI()); 
/* do some other work */ 
Double result=f.get(); 

lub

Future<Double> f=executorService.submit(MyClass::calculatePI); 
/* do some other work */ 
Double result=f.get(); 

Pytanie kiedy używaćCallable Hasn w ogóle się nie zmieniło.

Podobnie, pytanie kiedy używać Supplier nie jest uzależniona od sposobu jego realizacji, które jednak użyć API, czyli

CompletableFuture<Double> f=CompletableFuture.supplyAsync(MyClass::calculatePI); 
/* do some other work */ 
Double result=f.join();// unlike Future.get, no checked exception to handle... 
1

Pójdę przez scenariusz, w którym powinniśmy wykorzystać Supplier<LocalDate> zamiast LocalDate .

kod, który bezpośrednio dzwoni do metod statycznych jak LocalDate.now() jest bardzo trudne do testów jednostkowych. Rozważmy scenariusz, w którym chcemy jednostki przetestować metodę getAge() który oblicza wiek osoby:

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()); 
    } 
} 

Działa to dobrze w produkcji. Ale test jednostkowy będzie musiał albo ustawić datę systemu na znaną wartość, albo być aktualizowany co roku, aby oczekiwać, że zwrócony wiek zostanie zwiększony o jedno, oba dość przerażające rozwiązania.

Lepszym rozwiązaniem byłoby wprowadzenie testu jednostkowego w znanej dacie przy jednoczesnym umożliwieniu użycia kodu produkcyjnego pod numerem LocalDate.now(). Może coś takiego:

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); 
    } 

} 

Rozważ scenariusz, w którym urodziny osoby minęły od czasu utworzenia obiektu. Dzięki tej implementacji getAge() będzie oparty na momencie utworzenia obiektu Person zamiast bieżącej daty. Możemy rozwiązać ten problem za pomocą 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()); 
    } 
} 

Wyjście będzie poprawnie być:

John Doe is 5 
John Doe is 6 

Nasz test jednostka może wstrzyknąć "teraz" datę takiego:

@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()); 
} 
Powiązane problemy