2015-03-10 5 views
7

Ich habe folgende Klasse:Warum zwingt mich Lambda, ein einzelnes Elementarray anstelle des endgültigen Objekts zu verwenden?

public class Item{ 
    private String name; 
    //setter getter 
} 

und Sammlung von Artikeln. Ich möchte den Namen des letzten Artikels in der Sammlung erhalten. Um das zu tun, iteriere ich einfach über die gesamte Sammlung und verwende zuletzt. Das Problem ist, ich weiß nicht, warum es mich zwingt, ein Element String-Array zu verwenden.

Warum muss ich verwenden müssen:

String[] lastName = {""}; 
items.forEach(item -> lastName[0] = item.getName()); 
System.out.println(lastname[0]); 

statt:

final String lastName; 
items.forEach(item -> lastName = item.getName()); 
System.out.println(lastname); 
+1

Eine 'Sammlung' hat keine Reihenfolge, wenn Sie ein bestimmtes Element wünschen, werden Sie wahrscheinlich stattdessen eine'ArrayList' verwenden wollen. – DennisW

+3

'Nachname' ist' final' und Sie können einen Wert nicht mehr als einmal vergeben. Sie können es also nicht in einer Schleife auf der linken Seite verwenden. – nkr

+1

Siehe http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class –

Antwort

13

Sie nicht lastName ein String, weil eine lokale Variable machen, die in einem Lambda (oder anonyme innere Klasse verwendet wird) muss (effektiv) final (see here) sein, dh Sie können es nicht bei jeder Ausführung des Lambda in der .forEach Schleife überschreiben. Wenn Sie ein Array (oder ein anderes Wrapper-Objekt) verwenden, weisen Sie dieser Variablen keinen neuen Wert zu, sondern ändern nur einen Teil davon, so dass sie endgültig sein kann.

Alternativ können Sie reduce verwenden, um den letzten Punkt zu überspringen:

String lastName = items.stream().reduce((a, b) -> b).get().getName(); 

Oder, wie es in den Kommentaren erwähnt, skip den ersten n-1 Elemente und nehmen Sie die erste danach:

String last = items.stream().skip(items.size() - 1).findFirst().get().getName(); 
+0

Dies ist wahrscheinlich effizienter, da Sie nicht alle Elemente des Streams verarbeiten müssen: 'String lastName = collec.stream(). Skip (collec.size() - 1) .findFirst(). Get () .getName() ' –

4

final bedeutet eigentlich, dass Sie es einmal zuweisen müssen (und nur einmal, wie durch die Kompilierzeitanalyse garantiert). Zum Beispiel der folgende Code ist ungültig:

final String lastName; 
List<Item> items = new ArrayList<Item>(); 
items.add(new Item("only one element")); 
for (Item item:items) lastName = item.getName(); 

In Ihrem zweiten Lambda-Ausdruck, ordnet der Verbraucher zu lastName, die als final erklärt wurden:

final String lastName; 
items.forEach(item -> lastName = item.getName()); 

Da Variablen in einem Lambda-Ausdruck verwiesen wird, müssen sei effectively final (dh so dass das final Schlüsselwort zu seiner Definition hinzugefügt werden kann), das Löschen des final Schlüsselworts in der Deklaration von lastName hilft nicht: hier ist lastName effektiv endgültig, und der Verbraucher ordnet zu effektiv endgültige Variable.

String lastName; 
items.forEach(item -> lastName = item.getName()); 

Auf der anderen Seite, in dem ersten Ausdruck des effektiv endgültigen Variable ist lastName, die ein Array ist. Dann können Sie tun:

String[] lastName = {""}; 
    items.forEach(item -> lastName[0] = item.getName()); 

hier, weil Sie nicht auf die Variable effektiv endgültige Zuordnung, sondern nur seine Elemente zu modifizieren (denken Sie daran, dass konstant, was die Referenz, nicht seine Werte), wie es auch ist Der Fall im folgenden Beispiel:

Verwandte Themen