2016-10-25 4 views
5

Welcher Unterschied zwischen diesem Code?Wann sollten wir den Lieferanten in Java 8 verwenden?

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

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

Ich fange an, funktionale Schnittstellen in Java 8 zu lernen und verstehe nicht, welchen Nutzen Anbieter bietet. Wann und wie genau sollten wir ihn benutzen. Verbessert der Lieferant die Leistung oder die Vorteile auf Abstraktionsebene?

Vielen Dank für Ihre Antworten! Und es ist keine doppelte Frage, weil ich Suche verwendet habe und nicht gefunden habe, was ich brauche.

UPDATE 1: Sie meinen das?

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' gibt Ihnen "jetzt" zu dem Zeitpunkt, an dem 's2' zugewiesen wurde. 's1.get()' gibt Ihnen "jetzt" an dem Punkt, an dem Sie 'get()' aufrufen. Das ist vielleicht nicht das Gleiche, wenn Sie den "Lieferanten" irgendwo hingegeben haben, um ihn bei Bedarf zu verwenden. Wenn Sie später 's1.get()' erneut aufrufen, erhalten Sie erneut eine andere Zeit. – khelwood

+0

@Khelwood, ich habe es nicht bekommen und aktualisierte Frage, bitte überprüfen. – badCoder

+1

Verwenden Sie es, wenn Sie die Fähigkeit übergeben müssen, _mulpeple values_ in _andother method._ zu erhalten. Das sind ziemlich viele 100% der gültigen Anwendungsfälle. –

Antwort

2

Es verbessert definitiv nicht die Leistung. Ihre Frage ist ähnlich wie diese: Warum verwenden wir Variablen? Wir könnten einfach alles neu berechnen, wenn wir es brauchen. Recht?

Wenn Sie eine Methode oft verwenden müssen, aber es hat eine wortreichen Syntax.

Nehmen wir an, Sie haben eine Klasse mit dem Namen MyAmazingClass, und Sie haben eine Methode mit dem Namen MyEvenBetterMethod (die statisch ist), und Sie müssen es 15 Mal an 15 verschiedenen Positionen in Ihrem Code aufrufen. Natürlich können Sie so etwas wie ...

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

tun ... aber Sie können auch

Supplier<MyAmazingClass> shorter = MyAmazingClass::MyEvenBetterMethod; 

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

Sicher wäre das 'Lieferant '. Und es hat nichts mit Leistung zu tun oder wie wortwörtlich eine Methode ist. Es gibt einen semantischen Unterschied. – biziclop

7

Sie sind verwirrend funktionalen Schnittstellen und Verfahren Referenzen tun. Supplier ist nur eine Schnittstelle, ähnlich wie Callable, die Sie seit Java 5 kennen sollten, der einzige Unterschied ist, dass Callable.call erlaubt ist, Exception s zu werfen, im Gegensatz zu Supplier.get. Diese Schnittstellen werden also ähnliche Anwendungsfälle haben.

Jetzt sind diese Schnittstellen auch Funktionsschnittstellen, was bedeutet, dass sie als eine Methodenreferenz implementiert werden können, die auf eine vorhandene Methode verweist, die aufgerufen wird, wenn die Schnittstellenmethode aufgerufen wird.

Also, bevor Java 8, musste man

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

und jetzt schreiben, können Sie

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

oder

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

Die Frage wenn schreibenCallable hasn verwenden Hab mich überhaupt nicht verändert.

Auch die Frage, wenn Supplier zu verwenden ist nicht davon abhängig, wie Sie es implementieren, aber die API Sie verwenden, dh

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

ich durch ein Szenario gehen werde, wo wir Supplier<LocalDate> statt LocalDate verwenden sollten .

Code, der Aufrufe an statische Methoden wie LocalDate.now() direkt macht, ist sehr schwierig zu Unit-Test. Betrachten wir ein Szenario, in dem wir an die Einheit wollen testen ein Verfahren getAge(), die das Alter einer Person berechnet:

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

Das in der Produktion gut funktioniert. Aber ein Komponententest müsste entweder das Systemdatum auf einen bekannten Wert setzen oder jedes Jahr aktualisiert werden, um zu erwarten, dass das zurückgegebene Alter um eins erhöht wird, beides ziemlich abscheuliche Lösungen.

Eine bessere Lösung wäre, dass der Komponententest ein bekanntes Datum eingibt und der Produktionscode trotzdem LocalDate.now() verwendet werden kann. Vielleicht etwas in der Art:

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

} 

Betrachten Sie ein Szenario, in dem der Geburtstag der Person seit der Erstellung des Objekts abgelaufen ist. Bei dieser Implementierung basiert getAge() darauf, wann das Person-Objekt erstellt wurde und nicht auf dem aktuellen Datum. Wir können dies lösen, indem Supplier<LocalDate> mit:

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

Der Ausgang würde richtig sein:

John Doe is 5 
John Doe is 6 

Unser Unit-Test das "jetzt" Datum wie folgt injizieren:

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