Pokazywanie postów oznaczonych etykietą java8. Pokaż wszystkie posty
Pokazywanie postów oznaczonych etykietą java8. Pokaż wszystkie posty

06 kwietnia 2014

O java.util.stream przez przykład(zik) z StackOverflow i Java 8 API

0 komentarzy
Istnieje wiele sposobów na poznanie nowego języka programowania i jednym z moich typów jest aktywność na StackOverflow. Można pokusić się na odpowiadanie na pytania (to stopień najwyższy wtajemniczenia), można “lajkować” (to stopień podstawowy), można edytować pytania i odpowiedzi uzupełniając je o dodatkowe informacje, które pozyskuje się z komentarzy czy wskazywać duplikaty (to stopień średniozaawansowany). Sprawdziło mi się to podczas nauki narzędzia sbt, więc kwiecień i kolejne kilka miesięcy zamierzam współdzielić z aktywnością w obszarze Java 8 (etykieta java-8 na StackOverflow).

W zależności od etykiety i dnia roboty może być na kilka minut aż do wielogodzinnej nasiadówy. Nie jest to zwykle łatwa robota początkowo, ale z czasem idzie coraz łatwiej, a i przyjemności coraz więcej. Aktywność dotyczy w równym stopniu pytań i odpowiedzi. Można również uzupełniać opisy etykiet.

Wszystkie aktywności na StackOverflow są nagradzane punktami reputacji, odznakami i wiedzą w temacie. W zasadzie brak wad.

O StackOverflow (i GitHub, twitterze oraz w promocji reddit) będę mówił podczas mojej prezentacji StackOverflow, GitHub i twitter jako narzędzia profesjonalnego rozwoju programisty na DevCrowd’14. Gorąco zachęcam do udziału.

Wróćmy jednak do nauki nowej wersji Java 8.

Kiedy dzisiaj do mojej skrzynki trafiło zestawienie pytań z etykiety java-8 było w nim tylko jedno pytanie - How to dynamically do filtering in Java 8?. Niefortunnie, nie należy ono do najbardziej pouczających, ale odpowiedź już tak. Stuart Marks, który jest autorem odpowiedzi, postarał się o sporą dawkę wiedzy nt. lambd i "pochodnych" w Java 8. Zdecydowanie warto zapoznać się z odpowiedzią.

Weźmy chociażby "This can't be done with a simple filter(predicate) construct on a stream.”

java.util.Collection<E> jest podstawowym interfejsem w kolekcjach w Javie (od momentu pojawienia się ich, już w Java 1.2!). Dotyczy to struktur danych takich jak zbiory, listy, kolejki i mapy.

Zmianą w Java 8 jest dodanie m.in. metody default Stream<E> stream(), która wprowadza nas w świat strumieni. Kolekcja, na której wywołamy stream(), będzie źródłem danych.
Stream<Integer> ints = Arrays.asList(1,2,3).stream();
Mając Stream jesteśmy w domu. Drzwi “streamowe” otwarte.

Wezmę na tapetę pierwszą metodę z tercetu funkcyjnego - filter (obok map i reduce). Jej sygnatura to filter(Predicate predicate)
ints.filter(...)
Niestety moja wiedza dotycząca wartości przekazywanej do filter jest znikoma, bo pojawia się kolejna klasa z Java 8 - java.util.function.Predicate. Warto zajrzeć do javadoc, w którym napisano:

"This is a functional interface and can therefore be used as the assignment target for a lambda expression or method reference.”

Jakaś masakra! :) Kompletnie mi to nic nie mówi. Intelekt i wdzięk podpowiadają mi jednak, że gdzieś tam pojawiały się konstrukcje w stylu argument(y) -> ciało funkcji. To właśnie nazywają lambdą (a przynajmniej takie mam wyobrażenie jak taka lambda mogłaby wyglądać mając pewne doświadczenie w innych językach funkcyjnych - Clojure, Scala, F#).

Sprawdzam taką konstrukcję.
Stream<Integer> oddInts = ints.filter(n -> n % 2 != 0);
Powinienem otrzymać strumień liczb nieparzystych. Wciąż to jednak wyłącznie strumień i próba System.out.println na tym zwróci jedynie tekstową reprezentację referencji.
java.util.stream.ReferencePipeline$2@682a0b20
Pora zmaterializować strumień do strawniejszej postaci, np. wyświetlę wszystkie elementy strumienia.

Z pomocą przychodzi mi IntelliJ IDEA. Wystarczy napisać oddInts.forEach i wcisnąć Ctrl+Shift+Spacja, aby pojawiła się jedyna słuszna podpowiedź - o -> {}. I to jest dokładnie to, czego potrzebuję - mam wybór, ale zbiór niezbyt liczny, bo jednego elementu. Próbuję wypisać elementy na ekran z System.out.println.
oddInts.forEach(o -> System.out.println(o));
Ctrl+Shift+F10 w IntelliJ IDEA i dostaję na ekranie wynik:
1
3
Dokładnie taki, jaki oczekiwałem!

Ale to nie koniec. IntelliJ IDEA nie poprzestaje i podpowiada, że warto zamienić tę konstrukcję na...method reference. Nie mam pojęcia, o co chodzi, ale podążam za głosem IDEA. Alt+Enter i już jest.
oddInts.forEach(System.out::println);
Taki sposób nauki lubię. Chciałbym mieć jeszcze możliwość poznawania Java 8 API w środowisku REPL (na wzór Scali, F#, Clojure czy Groovy), ale to raczej pieśń przyszłości. Dobrze, że mam IntelliJ IDEA!

Pełna klasa gotowa do uruchomienia:
package pl.japila.java8;

import java.util.Arrays;
import java.util.stream.Stream;

public class Main {
    public static void main(String[] args) {
        Stream<Integer> ints = Arrays.asList(1, 2, 3).stream();
        Stream<Integer> oddInts = ints.filter(n -> n % 2 != 0);
        oddInts.forEach(System.out::println);
    }
}
A jak Tobie idzie poznawanie Java 8 API? Chętnie posłucham rad bardziej wytrwałych, którzy pierwsze dni mają już dawno za sobą.

30 marca 2014

Java Platform, Standard Edition 8 wydane, więc nauki czas ponownie zacząć, co?

0 komentarzy
Java Platform, Standard Edition 8 (w skrócie Java SE 8 czy po prostu Java 8) jest już powszechnie dostępne. Wiele się zmieniło i pewnie nie tylko ja chciałbym wiedzieć, na ile Java 8 jest warta poznania, po tym, kiedy zarzucono ją na rzecz innych języków programowania - Clojure, Scala, Groovy. Ty też chciał(a)byś wiedzieć, co się zmieniło w najnowszej wersji Java 8? Najlepiej zacząć prosto u źródła - JDK 8 Release Notes lub What's New in JDK 8. Jest tego sporo do czytania, co może świadczyć o niemałych zmianach i dłużej nauce. Taka nasza informatyczna dola...

Pierwsza ciekawostka dla mnie to nazewnictwo - Java SE 8 vs JDK 8. Nie było to tak oczywiste, bo Java SE 8 to specyfikacja, a JDK 8 jej implementacją od Oracle. Jakoś wcześniej mi to umykało. Tak przynajmniej napisano w dokumentacji.

Mówi się, że najlepszym sposobem na naukę nowego języka jest po prostu korzystać z niego. Dotyczy to języka mówionego i dobrze sprawdza się również z językiem programowania. W ten sposób właśnie zamierzam poznać Java 8. Na swoim blogu proponuję pracę z IntelliJ IDEA 13. Właśnie kilka dni temu wyszła wersja 13.1.1 i wystarczy pobrać bezpłatną wersję Community Edition.

Przy założeniu, że Java 8 została zainstalowana, w IDEA wybierasz Create New Project, a później Java, wciskasz Next, aż pojawi się panel, w którym po raz pierwszy wybieramy Java 8 jako środowisko pracy.

Image

Po stworzeniu projektu, upewnij się, że Project language level ustawiony jest na 8.0 - Lambdas, type annotations, etc.

Image

Mając projekt można zacząć rozpoznawać Java 8 API. Od czego zaczniemy? Może java.util.stream?! A nie inaczej, skoro jest to (wraz z lambdami) jedna z ważniejszych zmian w tej wersji języka.

Za Collections Framework Enhancements in Java SE 8:

"Classes in the new java.util.stream package provide a Stream API to support functional-style operations on streams of elements. The Stream API is integrated into the Collections API, which enables bulk operations on collections, such as sequential or parallel map-reduce transformations."

Zaczniemy właśnie od Stream API, bo rozpoznając ten kawałek Java 8 nie sposób nie dotknąć innych zmian jak chociażby wspomniane lambdy.

Przez Collections Framework Enhancements in Java SE 8 dochodzimy do…i tu znowu cała masa dokumentacji. Zachęcam do lektury New and Enhanced APIs That Take Advantage of Lambda Expressions and Streams in Java SE 8.

Na tej stronie można doświadczyć pierwszego spotkania ze zmianami w Java 8:
Integer[] intArray = {1, 2, 3, 4, 5, 6, 7, 8 };
List listOfIntegers =
    new ArrayList<>(Arrays.asList(intArray));
    System.out.println("Sum of integers: " +
        listOfIntegers
            .stream()
            .reduce(Integer::sum).get());
Zacznijmy od tego kawałka.

Na początku rzuciło mi się, dlaczego new ArrayList<>(Arrays.asList(intArray)) zamiast po prostu Arrays.asList(intArray)? Pewnie jakaś ukryta wiedza magiczna.

Dla adeptów Java 8 nową metodą w java.util.List<t>, a dokładniej java.util.Collection jest default Stream stream(), który (za javadoc):

"Returns a sequential Stream with this collection as its source."

  1. Po pierwsze, co oznacza default przy sygnaturze?
  2. Po drugie, czym jest Stream?
  3. Po trzecie, skoro stream() zwraca “a sequential Stream”, to czy istnieje inny rodzaj Stream, np. non-sequential?
Wrócimy do tego niebawem.