optional - argonavis.com.br · 1. expressões lambda • uma expressão lambda é uma função...

41
Optional

Upload: phamtu

Post on 15-Dec-2018

241 views

Category:

Documents


2 download

TRANSCRIPT

Optional

Objetivo• Esta apresentação explora as principais novidades do

Java 8 (que foi lançado em março)

• O foco será nas mudanças na sintaxe (lambdas), APIs (streams, data e hora) e ferramentas (nashorn)

• Não serão discutidas nesta apresentação mudanças na JVM, JavaFX, runtime (Compact profiles) segurança (JCA) nem monitoração (JMC)

Conteúdo• Parte I - Mudanças na sintaxe da linguagem • 1. Expressões lambda e referências para métodos • 2. Nova sintaxe para interfaces

• Parte II - Novas APIs • 3. Data e hora (java.time) • 4. Interfaces funcionais (java.util.function) • 5. API funcional (java.util.stream) • 5. Paralelismo (ForkJoinPool.common()) • 6. API reativa (java.util.concurrent.CompletableFuture)

• Parte III - Ferramentas • 7. JavaScript engine API e ferramenta jjs (Nashorn)

1. Expressões lambda• Uma expressão lambda é uma função anônima

• É abstração de uma operação, tratada como se fosse dados, permitindo que seja atribuída a variáveis e retornada/passada de/para métodos

• Tem origem no cálculo lambda (Alonzo Church, 1936) e é usado em programação desde Lisp (1958)

• Faz parte de várias linguagens populares: JavaScript, Scala, C#, Go, Swift, Smalltalk, Python

Programação funcional• Lambdas são uma abstração fundamental em linguagens

funcionais

• Em orientação a objetos, o estado de objetos é modificado por operações representadas por seus métodos

• Na programação funcional, operações são representadas por funções entre objetos imutáveis

• Embora não tenha sido concebida como linguagem funcional, é possível programar em Java usando o paradigma funcional

• A API de streams facilita a programação funcional em Java 8

Métodos anônimos em Java• Desde Java 1.2 pode-se implementar interfaces anônimas

Runnable tarefa = new Runnable() { @Override public void run() { System.out.println("Hello!"); } }

• Java 8 vai mais longe com métodos anônimos (lambdas) Runnable tarefa = () -> { System.out.println("Hello!"); }

• As chaves são opcionais neste caso: Runnable tarefa = () -> System.out.println("Hello!");

Sintaxe de expressões lambda• Novo operador -> (seta) separa parâmetros e expressão • Variáveis usadas no bloco da expressão lambda devem ser

efetivamente finais (imutáveis) • Parenteses: obrigatórios se há mais de um parâmetro ou nenhum:

• p -> p/2; // mesmo que (p) -> p/2 • () -> 3; // parênteses necessários • (a,b) -> a+b; // parênteses necessários

• Declarar tipo dos parâmetros é opcional (se for possível inferir) • int r = p -> p/2; // Implícito: (int p) -> p/2;

• Chaves e a instrução return: opcionais se apenas uma instrução • int r = (a,b) -> a+b; // (int a, int b) -> { return a+b;}

Referências de métodos• O operador :: declara referências para métodos e construtores • Há quatro tipos

• Classe::metodoEstatico // referência para metodo estatico • Classe::metodoDeInstancia // ref. para método de um objeto qualquer • objeto::metodoDeInstancia // ref. para método de um objeto específico • Classe::new // referência para construtor

• Exemplos System.out::println Comparator::compare HashSet<Integer>::new

• Uma referência pode substituir uma expressão lambda; os argumentos são obtidos por inferência (as duas formas abaixo são equivalentes) • Consumer<String> f1 = s -> System.out.println(s); • Consumer<String> f2 = System.out::println; interfaceConsumer<T>{

voidaccept(Tt);}

UsaráparâmetroTdométodoporinferência

2. Sintaxe para interfaces• Interfaces, a partir de Java 8, podem conter métodos com

implementações, desde que sejam • Estáticos (com modificador static) • Default (com novo modificador default)

interface Template { default String transform(String s) { return tag(s); } static String tag(String s) { return "<tag>"+s+"</tag>"; } }

Upper u = new Upper(); Plain p = new Plain(); System.out.println(Template.tag("A")); System.out.println(u.transform("B")); System.out.println(p.transform("C"));

class Plain implements Template { @Override public String transform(String s) { return s; } }

class Upper implements Template {}

<tag>A</tag><tag>B</tag>C

Métodos default• Métodos default não precisam ser sobrepostos • Conflito pode haver se uma classe implementa duas interfaces com

métodos default iguais • Neste caso é necessário sobrepor o método escolhendo qual

implementação usarinterface A { default void m() { System.out.println("A"); } } interface B { default void m() { System.out.println("B"); } }

class C implements A, B { public void m() { B.super.m(); // escolhendo implementação B } }

3. Nova API de datas• Cinco pacotes (java.time + 4 subpacotes) com novas abstrações

para intervalos, períodos, datas, meses, eras, anos, unidades, fusos horários, formatação, etc.

• Principais abstrações estão no pacote raiz java.time • LocalDate, LocalTime, LocalDateTime • ZoneId, ZonedDateTime • OffsetDateTime, OffsetTime, ZoneOffset • Clock, Duration, Instant, Period • Month, Year, YearMonth, MonthDay, DayOfWeek

• Objetos são criados com factory methods

Data e Hora• LocalDate, LocalTime e LocalDateTime representam data ISO

8601 (calendário Gregoriano)LocalDate date1 = LocalDate.now();

System.out.println(date1); int day = date1.getDayOfMonth();

System.out.println(day); LocalDate date2 = LocalDate.parse("2014-08-20");

System.out.println(date2); LocalDateTime dt = date2.atTime(13, 59);

System.out.println(dt); LocalTime time = dt.toLocalTime();

System.out.println(time); LocalTime tonight = time.plusHours(8);

System.out.println(tonight); LocalDate someDay = LocalDate.of(1995,5,1);

System.out.println(someDay);

2014-08-16

16

2014-08-20

2014-08-20T13:59

13:59

21:59

1995-05-01

Formatação e parsing• Métodos toString() default utilizam formatações padrão sem a

necessidade de usar formatadores

• É possível personalizar os formatos para formatação e parsing usando DateTimeFormatter.ofPattern()

DateTimeFormatter formato1 = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm"); DateTimeFormatter formato2 = DateTimeFormatter.ofPattern("MMMM d, yyyy hh:mm a"); LocalDateTime ldt = LocalDateTime.parse("20/08/2014 13:59", formato1); System.out.println(ldt);

String formatted = ldt.format(formato2); System.out.println(formatted);

2014-08-20T13:59

August 20, 2014 01:59 PM

Duração e período• Instant representa um instante qualquer

Instant agora = Instant.now(); // 2014-08-16T14:44:07.989809Z Instant depois = agora.plusSeconds(30); // 2014-08-16T14:44:37.989809Z

• Duration representa uma duração de tempo (30 segundos) Duration duracao = Duration.between(agora, depois); // PT30S

• Period representa um intervalo entre duas datas (4 meses, 16 dias) Period periodo = Period.between(LocalDate.now(), LocalDate.of(2015, 1, 1)); // P4M16D

• Clock permite relógios alternativos (com offset, com resolução de minutos, etc) Clock relogioSistema = Clock.systemUTC(); // Default SystemClock[Z] Clock tictac = Clock.tickMinutes(ZoneId.systemDefault()); // Conta apenas minutos Instant start = tictac.instant(); // 2014-08-16T17:54:00Z Duration elapsed = Duration.between(start, start.plusSeconds(126)); // PT2M6S

Interface comum• Prefixos comuns a métodos estáticos em classes do pacote

• of - método de fábrica: ZoneId zone = ZoneId.of("GMT"); • parse - método de fábrica: LocalDate d = LocalDate.parse("2014-08-19");

• Prefixos comuns a métodos de instância • get - retorna o valor de algo: int dia = dateTime.getDayOfMonth(); • is - retorna true ou false: boolean bissexto = year.isLeap(); • with - retorna uma cópia modificada: Clock fuso2 = fuso1.withZone(zone); • plus - adiciona uma quantidade: Period p2 = p1.plusDays(5); • minus - subtrai uma quantidade: Instant s2 = s2.minusSeconds(30); • to - converte objeto em outro tipo: int segundos = localTime.toSecondOfDay(); • at - combina objeto com outro: LocalDate data = monthDay.atYear(2014);

4. Interfaces funcionais• Interface funcional: interface com um método abstrato • Se houver outros métodos devem ser static ou default • Podem ser implementadas usando expressões lambda!

• Java 8 introduziu a anotação @FunctionalInterface para identificar interfaces funcionais em tempo de compilação • Haverá erro de compilação se interface não for funcional

• O uso de interfaces funcionais em lambdas permite alto grau de reuso: lambdas ignoram tipos recebidos/retornados

• O pacote java.util.function contém uma coleção de interfaces padrão para reuso

java.util.function• 43 interfaces genéricas de propósito geral • Principais interfaces • Predicate<T>: Recebe (T); Retorna boolean • BiPredicate<T,U>: Recebe (T,U); Retorna boolean • Consumer<T>: Recebe (T); Retorna void • Supplier<T>: Recebe (); Retorna T • Function<T,R>: Recebe (T); Retorna R • BiFunction<T,U,R>: Recebe (T,U); Retorna R • UnaryOperator<T>: Recebe (T); Retorna T • BinaryOperator<T, T>: Recebe (T, T); Retorna T

Uso de interfaces funcionais• O nome do método da interface funcional é irrelevante para criar

expressões lambda, já que é anônimo; tipos também são irrelevantes • O mais importante são: quantidade de argumentos recebidos e

se retorna ou não algum valor • Uma expressão usando Supplier<T> com T = String

• Uma expressão usando Function<Integer,String>

• Uma expressão usando BiFunction<T, U, R>

String produto = () -> "Hello!";

String resultado = (a,b) -> a + b; // concatena ou soma

Integer tamanho = s -> s.length(); // recebe String, retorna Integer

Métodos que recebem lambdas• Um método que recebe uma expressão lambda declara

receber uma interface funcional

• Pode-se implementar uma função anônima e passá-la como argumento, da mesma forma como são passados os dados

public Integer calcular(Function<Integer> funcao, Integer operando) { return funcao.apply(operando); }

System.out.println( calcular( n -> n * n, 4); System.out.println( calcular( n -> n / 2, 16);

5. Streams• A classe java.util.stream.Stream fornece uma API para

programação funcional baseado na concatenação de operações lazy processadas quando uma operação terminal é chamada

• Um Stream conduz elementos de uma fonte através de uma pipeline de operações, produzindo um resultado sem modificar a fonte

• Elementos são processados uma única vez (o stream é consumido)

• Streams podem ser infinitos (operações intermediárias podem limitar os dados durante o processamento)

• Streams podem ser criados/obtidos de várias formas

Como criar um Stream• Um stream pode ser criado a partir de métodos de fábrica:

generate(), iterate(), of(), etc. Stream<String> letras = Stream.of("X", "T", "S", "P"); Stream<Integer> infinito = Stream.generate(()-> new Random().nextInt(100));

• É mais comum criar um stream a partir de uma coleção usando o método stream() ou parallelStream() List<Integer> colecao = new ArrayList(); // ... Stream<Integer> colecao.stream(); Stream<Integer> colecao.parallelStream(); // paralelismo

• Um stream pode ser transformado via operações intermediárias (lazy) e uma operação terminal (eager - que encerra o stream)

Operações de um stream• As operações intermediárias (lazy) são executadas apenas depois

que o stream é terminado (puxando os dados: método "pull") • Após uma operação terminal o stream não pode ser reusado • As operações recebem interfaces funcionais • Algumas operações. Operações intermediárias retornam Stream: • filter(Predicate<T>): intermediária • map(Function<T,U>): intermediária • flatMap(Function<T,Stream<R>>): intermediária • reduce(BinaryOperator<T>): terminal • forEach(Consumer<T>): terminal • collect(Collector<T,A,R>): terminal

Outrasoperaçõesterminais:min(),max(),count(),etc.Outrasintermediárias:skip(),peek(),distinct(),etc.

Filter• A operação intermediária filter(Predicate<T>) remove

do stream elementos que não combinam com a função

List<Integer> numbers = Arrays.asList(new Integer[] {4,1,9,6,8,3,5});

numbers.stream() .filter(n -> n > 5) .forEach(System.out::println);

Imprime968

Map• A operação intermediária map(Function<T,U>)

recebe uma função que realiza uma transformação no stream (e pode converter um tipo em outro):

List<Integer> numbers = Arrays.asList(new Integer[] {4,1,9,6,8,3,5});

numbers.stream() .filter(n -> n > 5) .map(n -> n * n) .forEach(System.out::println);

Imprime813664

ForEach• O método forEach(Consumer<T>) foi adicionado em Iterable

e está disponível para qualquer implementação (ex: Collection): list.forEach(System.out::println);

• Stream também implementa Iterable e pode chamar forEach(): Stream<Integer> numeros = Stream.of(1,2,3,4); numeros.map(s->s*2).forEach(System.out::println);

• ForEach é uma operação terminal (depois de chamada, puxa a execução do stream não permitindo novos métodos): numeros.filter(n->n<3); // exceção (stream encerrado)

Reduce• reduce(BinaryOperator<T>) é uma operação terminal que retorna

resultado da operação de combinação sobre valores acumulados • Há três diferentes versões de reduce (com 1, 2 ou 3 args) • O resultado pode ser do mesmo tipo (T), outro tipo ou

Optional<T> (objeto que encapsula T ou null)

List<Integer> numbers = Arrays.asList(new Integer[] {4,1,9,6,8,3,5}); Optional<Integer> resultado = numbers.stream() .filter(n -> n > 5) .map(n -> n * n) .reduce((acum, oper) -> a+b); int soma = resultado.get(); // 181 (81+36+64)

Collect• collect(Collector<T,A,R>) é uma operação terminal que executa uma

redução mutável nos elementos do stream. O coletor constrói o resultado usando funções de acumulação e combinação.

• collect() pode ser usado para reduzir um stream a uma coleção

• Classe utilitária Collectors contém vários algoritmos implementados (toList(), groupingBy(), etc.)

Map<String, List<Movie>> directors = movieList.stream() .collect(Collectors.groupingBy(Movie::getDirector));

List<String> titles = movieList.stream() .map(movie -> movie.getTitle() + " (" +movie.getDirector()+ ")") .collect(Collectors.toList());

FlatMap• flatMap(Function<T,Stream<R>>) é uma operação

intermediária que "achata" um stream de streams

List<String> titlesAndDirectors = movieList.stream() .flatMap(movie -> Stream.of(movie.getTitle(), movie.getDirector())) .collect(Collectors.toList());

Exceções e valores nulos• Streams não podem deixar escapar exceções checadas • É preciso capturar a exceção • Pode-se lançar uma exceção de runtime • Ideal é devolver um objeto comum que contenha informações

que permitam lidar com o problema • Objetos Optional podem ser usados para lidar com valores nulos

public Optional<Integer> transformar(Integer valor) { try { // operações sobre valor return Optional.of(resultado); } catch (Excecao e) { return Optional.empty(); }

} int valor = 123; Optional<Integer> op = transformar(valor); Integer resultado = op.orElse(valor);

Retornaresultadoou123(seocorrerexceçãoeOptionalestivervazio)

Streams de primitivos• java.util.stream também contém um conjunto de Streams de primitivos

como DoubleStream, IntStream e LongStream • Streams comuns podem ser convertidos em streams de primitivos:

mapToInt(), mapToDouble(), etc. • IntStream stream = lista.stream().mapToInt(n->n);

• Possuem métodos especiais para manipular de primitivos e obter estatísticas

IntSummaryStatistics example = Stream.of(9,4,8,2,15,82,91,77,53,27,13) .mapToInt(n->n).summaryStatistics();

System.out.printf("Count: %d, Max: %d, Min: %d, Avg: %f, Sum: %d", example.getCount(), example.getMax(), example.getMin(), example.getAverage(), example.getSum());

6. Paralelismo• O Java 8 introduziu um ForkJoinPool comum para uso em

operações paralelas • Forma recomendada de uso (new ForkJoinPool() passa

a ser um anti-pattern) • Configurável via propriedades do sistema • Pool default para streams paralelos e operações

assíncronas com CompletableFuture • Para obter uma referência ao pool

• ForkJoinTasks podem ser chamadas sem informar um poolForkJoinPool poolComum = ForkJoinPool.common();

task.invoke(); // em vez de pool.invoke(task) usa poolComum

7. CompletableFuture• Uma API para programação reativa similar à API de streams

(mas baseada em "push" - o oposto da API de streams, que é baseada em "pull")

• Objetos Future que podem ser concatenados em série • Contém 36 métodos que executam tarefas assíncronas e

retornam CompletableFuture: recebem interfaces funcionais • Cada CompletionStage executa um estágio da tarefa

CompletionStage

CompletionStage

CompletionStage CompletableFuture

CompletionStageFuture

Como criar um CompletableFuture• Usando o construtor default

CompletableFuture<Integer> future = new CompletableFuture<>(); future.complete(5);

• Usando métodos de fábrica com Runnable ou Supplier<V> CompletableFuture<Void> cf1 = CompletableFuture.runAsync(() -> { System.out.print("Etapa assíncrona");});

CompletableFuture<String> cf2 = CompletableFuture.supplyAsync(() -> { return "Etapa assíncrona";});

Concatenação de estágios• Estágios seguintes podem processar dados do estágio anterior

(com tarefas Consumer<T>, Function<T,R> e Runnable)

• Tarefas podem rodar em threads novos (disparados pelo ForkJoinPool comum) ou escolher um executor próprio

• Há métodos para combinar múltiplos estágios anteriores

CompletableFuture.supplyAsync(() -> event.get() ) .thenApply( x -> x * x ) .thenAccept ( x -> System.out.println(x) ) .thenRun( () -> System.out.println("Done!") );

8. Nashorn• JavaScript engine (javax.script.ScriptEngine) que permite embutir

JavaScript dentro de aplicações Java e vice-versa

• Código Java pode executar JavaScript e vice-versa e compartilhar dados e operações

• Mapeamento automático entre linguagens: tipos são convertidos automaticamente, getters/setters acessíveis como propriedades JSON, coleções Java são tratadas como arrays JavaScript

• Também inclui uma ferramenta de linha de comando jjs

• Interpretador para programação interativa (REPL)

• Permite escrever código JavaScript usando classes e objetos Java

Chamando JavaScript

final ScriptEngineManager factory = new ScriptEngineManager(); final ScriptEngine engine = factory.getEngineByName("nashorn"); try { engine.eval("print('JavaScript hello!');"); } catch (ScriptException e) { e.printStackTrace(); }

• É necessário obter uma instância de javax.script.ScriptEngine • Método eval() executa código JavaScript contido em String

JavaScript hello!OUTPUT

Chamando função externa

final ScriptEngineManager factory = new ScriptEngineManager(); final ScriptEngine engine = factory.getEngineByName("nashorn"); try { engine.eval(new FileReader("target/classes/test.js")); Invocable inv = (Invocable)engine; inv.invokeFunction("printText", "Something"); } catch (ScriptException | FileNotFoundException | NoSuchMethodException e){ ... }

• eval() pode carregar um script externo • Invokable.invokeFunction() chama função JavaScript

existente no script carregadofunction printText(text) { print("JavaScript prints: " + text); }

test.js

JavaScript prints: SomethingOUTPUT

Acessando objeto (JSON)

final ScriptEngineManager factory = new ScriptEngineManager(); final ScriptEngine engine = factory.getEngineByName("nashorn"); try { ScriptObjectMirror mirror = (ScriptObjectMirror) engine.eval(new FileReader("target/classes/movie.js")); System.out.println(mirror.getMember("title")); System.out.println(mirror.getMember("director")); System.out.println(mirror.getMember("year"));

} catch (ScriptException | FileNotFoundException | NoSuchMethodException e) { e.printStackTrace(); }

var movie = { "title" : "The Shining", "director" : "Kubrick", "year" : 1980 } movie;

movie.js

The ShiningKubrick1980

OUTPUT

Chamando Java de JavaScript• Função Java.type("pacote.Class") carrega tipos Java e permite usá-los em

JavaScript var ArrayList = Java.type("java.util.ArrayList"); var defaultSizeArrayList = new ArrayList; var customSizeArrayList = new ArrayList(16);

– Pacotes e classes podem ser importados com importPackage() e importClass() (exemplos do tutorial da Oracle [8]):

load("nashorn:mozilla_compat.js"); // script com funções importPackage(java.awt); importClass(java.awt.Frame); var frame = new java.awt.Frame("hello"); frame.setVisible(true); print(frame.title);

– Vários outros recursos (veja referência [8] no final para mais detalhes)

Referências• Java 8

• [1] http://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html • [2] Apresentações do JavaONE 2013 em http://parleys.com/?reqp=1&reqr=pJ1vLKVhpTW6

• Lambda • [3] https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html

• Date/Time API • [4] https://docs.oracle.com/javase/tutorial/datetime/

• Streams • [5] https://docs.oracle.com/javase/tutorial/collections/streams/index.html

• CompletableFuture & paralelismo • [6] http://www.nurkiewicz.com/2013/05/java-8-definitive-guide-to.html • [7] https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/changes8.html

• Nashorn • [8] https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/prog_guide • [9] https://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn

Atualização Java 8HelderdaRocha

21deagostode2014

Exemplosdecódigoserãodisponibilizadosemgithub.com/argonavis_br

www.argonavis.com.br www.summa.com.br