Stream Grouping Beispiel
public class StreamGroupingExercice { public static void main(String[] args) throws IOException { List<String> strings = Files.readAllLines(Paths.get("names.txt")); List<String> names = strings.stream().flatMap(s -> Arrays.asList(s.split(", ")).stream()).collect(Collectors.toList()); Map<String, Long> groupedCountedNames = names.stream().peek(item -> System.out.println("Schritt 1: " + item)) .collect(Collectors.groupingBy(Function.identity(), Collectors.counting() )); assertThat(groupedCountedNames).containsEntry("Hans", 2L); assertThat(groupedCountedNames).containsEntry("Paul", 1L); } }
Reihen generiern mit Stream-API
public class LerneUnendlicheReihen { public static void main(String[] args) { IntStream reihe = IntStream.iterate(100, i -> ++i); reihe.limit(100).forEach(System.out::println); /** * Output: * 100 * 101 * ...usw... * 199 */ "abcdefg".chars().forEach(System.out::println); System.out.println("Nun mit Integer Objekten:"); AtomicInteger atomicInteger = new AtomicInteger(100); Stream<Integer> objektReihe = Stream.generate(atomicInteger::getAndIncrement); objektReihe.limit(10).forEach(System.out::println); /* Ausgabe: Nun mit Integer Objekten: 100 101 * ...usw... 109 */ } }
Stream Reduce anwenden, um Auswertungen über Elemente zu machen
import static org.assertj.core.api.Assertions.assertThat; public class LerneReduce { public static void main(String[] args) { List<String> bros = TestDataProvider.getBrothersInArms(); assertThat( bros.stream().reduce((a, b) -> a + ", " + b).get() ).isEqualTo("Walther, Hans, Erich, Paul"); IntStream oneTo3 = IntStream.iterate(1, i -> ++i).limit(3); assertThat( oneTo3.reduce((i1, i2) -> i1 + i2).getAsInt() ).isEqualTo(6); } }
String-Joining mit Stream-API
import static org.assertj.core.api.Assertions.assertThat; public class LerneJoining { public static void main(String[] args) { List<String> bros = TestDataProvider.getBrothersInArms(); String brosString = bros.stream().collect(Collectors.joining(", ")); //Collectors.joining(...) assertThat(brosString).isEqualTo("Walther, Hans, Erich, Paul"); } }
Guava
Schnelles Initialisieren von Listen, Sets und Maps
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; import com.google.common.collect.Sets; public class LerneGuava1 { @Test public void liste(){ //List List<String> guavaList1 = ImmutableList.of("Walther", "Hans", "Erich", "Paul"); List<String> guavaList2 = Lists.newArrayList("Walther", "Hans", "Erich", "Paul"); List<String> javaList1 = Arrays.asList("Walther", "Hans", "Erich", "Paul"); //Sets: Set<String> guavaSet1 = ImmutableSet.of("Walther", "Hans", "Erich", "Paul"); Set<String> guavaSet2 = Sets.newHashSet("Walther", "Hans", "Erich", "Paul"); Set<String> javaSet = new HashSet<String>(Arrays.asList("Walther", "Hans", "Erich", "Paul")); //Maps: Map<String, Integer> nameWithAge = ImmutableMap.of("Hans", 62, "Walter", 61, "Erich", 64); assertThat(nameWithAge.get("Hans").equals(62)); } }
Comparator – Schnelles Erstellen
Comparator personaComparator = Comparator.<strong>comparing</strong>(Persona::getAge).<strong>thenComparing</strong>(Persona::getName))
import static org.assertj.core.api.Assertions.assertThat; public class LerneComparator { final static Persona JONA = new Persona(7, "Jona"); final static Persona ZORA = new Persona(11, "Zora"); final static Persona NATASHA = new Persona(22, "Natasha"); final static Persona[] personas = {JONA, ZORA, NATASHA}; public static void main(String[] args) { List<Persona> personList = Arrays.asList(personas); System.out.println("Mit Comparator:"); Collections.sort(personList, Comparator.comparing(Persona::getAge).thenComparing(Persona::getName)); assertThat(personList).containsExactly(JONA, ZORA, NATASHA); System.out.println("Mit Comparator (reverse order):"); Collections.sort(personList, Comparator.comparing(Persona::getAge).thenComparing(Persona::getName).reversed()); assertThat(personList).containsExactly(NATASHA, ZORA, JONA); System.out.println("Mit Comparable Objects: "); Collections.sort(personList); //Persona must implement Comparable-Interface assertThat(personList).containsExactly(JONA, NATASHA, ZORA); } }
AssertJ – Lesbarere Tests mit vielen Zusatzfunktionen
Based on: http://joel-costigliola.github.io/assertj/assertj-core-features-highlight.html#field-by-field
Cool 1: Fluent API ist gut lesbar
String name = "Hans"; assertThat(name).as("Der Name muss 'Hans' sein").isEqualTo("Hans");
Cool 2: Features wie z.B. Filtering
List<Person> persons = Arrays.asList(new Person(20, "Nora"), new Person(22, "Guliver"), new Person(66, "Siegfried")); assertThat(persons).filteredOn("name", "Nora").extracting("name").containsOnly("Nora");
Cool 3: Soft Assertions – Mehrere assertions miteinander auswerten, bevor allenfalls abgebrochen wird
@Test public void softAssertion(){ List<Person> persons = Arrays.asList(new Person(20, "Nora"), new Person(22, "Guliver"), new Person(66, "Siegfried")); SoftAssertions.assertSoftly(softAssertions -> { softAssertions.assertThat(persons.size() > 8).as("Groesse der Personenliste").isTrue(); softAssertions.assertThat(persons).as("Must be Reto having Noras Name?") .filteredOn("name", "Nora").extracting("name").containsOnly("Reto"); }); }
Kumulierbare/verknüpfbare Kriterien erstellen mit Hilfe von Functional interfaces/Lambda (Übung) / Fluent Interface / Method Chaining
Java 8 – Lösung:
public interface Specification<T> { default Specification<T> and(Specification spec){ Specification<T> outerSpec = this; return obj -> outerSpec.isSatisfiedBy(obj) && spec.isSatisfiedBy(obj); } boolean isSatisfiedBy(T obj); }
Erklärung:
Eine Spezifikation definiert (in isSatisfiedBy(T obj)) wann sie erfüllt ist.
Die and(..) Methode gibt eine Kombination aus der vorliegenden (this) und der übergebenen Spezifikation zurück.
Aufruf:
import static org.assertj.core.api.Assertions.assertThat; public class Test { public static void main(String[] args) { Specification<String> istHans = s -> s.equals("Hans"); Specification<String> laengeNichtNull = s -> !s.isEmpty(); Specification<String> kombinierteSpezifikation = istHans.and(laengeNichtNull); assertThat(kombinierteSpezifikation.isSatisfiedBy("Hans")) .as("Kombinierte Spezifikation trifft zu").isTrue(); assertThat(kombinierteSpezifikation.isSatisfiedBy("Peter")) .as("Kombinierte Spezifikation trifft nicht auf 'Peter' zu").isFalse(); assertThat(laengeNichtNull.isSatisfiedBy("Peter")) .as("Nur die eine Spezifikation (nicht leer) trifft zu").isTrue(); /** * Spezifikation kann auch anders parametrisiert werden: */ Specification<Integer> groesser7 = i -> i.compareTo(7) > 0; Specification<Integer> istGerade = i -> (i & 1) == 0; assertThat(groesser7.and(istGerade).isSatisfiedBy(8)).isTrue(); assertThat(groesser7.and(istGerade).isSatisfiedBy(9)).isFalse(); assertThat(groesser7.and(istGerade).isSatisfiedBy(6)).isFalse(); } }
Begriffe:
Fluent API: Wenn Aufrufe aneinander gekettet werden können, dass dabei eine natürlich verständliche Aussage wiedergegeben wird, z.B.:
assertThat(laengeNichtNull.isSatisfiedBy("Peter")) .as("Nur die eine Spezifikation (nicht leer) trifft zu").isTrue();
Method Chaining: Verkettung von Methoden-Aufrufen (wie eben gezeigtes Beispiel). Dabei wird oft ein Object derselben Klasse wir das Objekt auf der die Methode aufgerufen wird zurück gegeben:
public class Klasse { ... //Method that allows chaining Klasse addName(String s){ ... } }
Zum Vergleich eine Version ohne Java 8 Fetures:
public interface Specification<T> { Specification<T> and(Specification spec); boolean isSatisfiedBy(T obj); }
Da Default-Methoden als Sprachmittel fehlen wird die Methode and(..) in einer abstrakten Klasse implementiert:
public abstract class SpecificationImpl<T> implements Specification<T> { public Specification<T> and(Specification spec){ Specification<T> outerSpec = this; return new SpecificationImpl<T>() { @Override public boolean isSatisfiedBy(T obj) { return outerSpec.isSatisfiedBy(obj) && spec.isSatisfiedBy(obj); } }; } }
Aufruf:
Die konkreten Spezifikationen müssen (z.B. als anonyme) Klassen ausprogrammiert werden:
public class Test { public static void main(String[] args) { SpecificationImpl<String> istHans = new SpecificationImpl<String>() { @Override public boolean isSatisfiedBy(String s) { return s.equals("Hans"); } }; SpecificationImpl<String> laengeNichtNull = new SpecificationImpl<String>() { @Override public boolean isSatisfiedBy(String s) { return !s.isEmpty(); } }; Specification<String> kombinierteSpezifikation = istHans.and(laengeNichtNull); assertThat(kombinierteSpezifikation.isSatisfiedBy("Hans")) .as("Kombinierte Spezifikation trifft zu").isTrue(); assertThat(kombinierteSpezifikation.isSatisfiedBy("Peter")) .as("Kombinierte Spezifikation trifft nicht auf 'Peter' zu").isFalse(); assertThat(laengeNichtNull.isSatisfiedBy("Peter")) .as("Nur die eine Spezifikation (nicht leer) trifft zu").isTrue(); /** * Spezifikation kann auch anders parametrisiert werden: */ SpecificationImpl<Integer> groesser7 = new SpecificationImpl<Integer>() { @Override public boolean isSatisfiedBy(Integer i) { return i.compareTo(7) > 0; } }; Specification<Integer> istGerade = new SpecificationImpl<Integer>() { @Override public boolean isSatisfiedBy(Integer i) { return (i & 1) == 0; } }; assertThat(groesser7.and(istGerade).isSatisfiedBy(8)).isTrue(); assertThat(groesser7.and(istGerade).isSatisfiedBy(9)).isFalse(); assertThat(groesser7.and(istGerade).isSatisfiedBy(6)).isFalse(); } }
Zum Vergleich nochmals:
< Java 8: | Java 8: Lambda |
---|---|
SpecificationImpl<String> laengeNichtNull = new SpecificationImpl<String>() { @Override public boolean isSatisfiedBy(String s) { return !s.isEmpty(); } }; |
Specification<String> istHans = s -> s.equals("Hans"); |
ForEach von Java 8 Collections mit Lambda: Limitierung bei int Laufvariable/Counter
public class LambdaForEach { public static void main( String[] args ) { List<String> myList = Arrays.asList("A", "B"); // NOT possible: //Error: Variable used in lambda expression should be final or effectively final // int i = 1; // myList.forEach(item -> System.out.println("Item number " + i++ + "is " + item )); //Stattdessen soll man das so machen: final AtomicInteger atomicInteger = new AtomicInteger(0); myList.forEach(item -> System.out.println("Item number " + atomicInteger.incrementAndGet() + " is " + item )); //Error: Variable used in lambda expression should be final or effectively final //Warum denn nicht so? int i = 0; for(String item : myList){ System.out.println("Item number " + ++i + " is " + item ); } } } // Output: // Item number 1 is A // Item number 2 is B // Item number 1 is A // Item number 2 is B
Diskussion auf:
https://stackoverflow.com/questions/28790784/java-8-preferred-way-to-count-iterations-of-a-lambda
NullPointerException-sicherer access über mehrere Relationen hinweg.
Das Probem:
Ein Feld in Sub3 gehalten von Sub2, wiederum gehalten von Sub1 muss geholt werden. Vorliegend ist Sub1, wobei schon die Ref auf Sub2 NULL sein könnte. –> Folge: Mühsames Abfragen auf NULL.
Generische Lösung:
Honour to: http://winterbe.com/posts/2015/03/15/avoid-null-checks-in-java/
- Eine Methode programmieren, die enen Supplier als Parameter erhält.
/** * Used to access a field down along a hierarchical path without having to code all null checks. * @param resolver the function defining the access-path * @param <T> the result type of the optional returned * @return An optional for the desired result type. */ public static <T> Optional<T> getNullPointerExSave(Supplier<T> resolver) { try { T result = resolver.get(); return Optional.ofNullable(result); } catch (NullPointerException e) { return Optional.empty(); } }
2. Für hierarchischen (NullPointerException-sicheren) Zugriff:
Sub3 retrievedSub = getNullPointerExSave(() -> sub1.getSub2().getSub3()).orElse(null)