BT

Disseminando conhecimento e inovação em desenvolvimento de software corporativo.

Contribuir

Tópicos

Escolha a região

Início Notícias Tudo sobre o Java 8

Tudo sobre o Java 8

A TechEmpower, uma empresa de desenvolvimento de aplicações customizadas com sede em El Segundo, Califórnia, postou recentemente um post chamado "Tudo sobre o Java 8". No post há um resumo completo das mudanças que o desenvolvedor encontrará no Java 8. Aqui está uma visão geral do post.

 

Melhorias nas interfaces

Agora as interfaces podem definir métodos static. Por exemplo, a classe java.util.Comparator agora possui o método static naturalOrder:

  public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
    return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
  }

Agora as interfaces podem fornecer métodos padrões. Isso permite que o desenvolvedor adicione novos métodos sem quebrar os códigos existentes que implementam a interface. Por exemplo, o padrão forEach foi incluído na interface java.lang.Iterable:

  public default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
      action.accept(t);
    }
  }


Vale ressaltar uma interface não pode fornecer uma implementação padrão para os métodos da classe Object.

 

Interfaces funcionais

Uma interface funcional é uma interface que define apenas um método abstrato. A anotação FunctionalInterface foi adicionada para indicar que uma interface tem a intenção de ser uma interface funcional. Por exemplo, a interface java.lang.Runnable é uma interface funcional.

  @FunctionalInterface
  public interface Runnable {
    public abstract void run();
  }

Note que o compilador do Java irá tratar qualquer interface que atenda à definição de interface funcional mesmo que não possua a anotação FunctionalInterface. Porém quando anotada com FunctionalInterface o compilador verifica se há apenas um método.

 

Lambdas

A característica mais importante das interfaces funcionais é que elas podem ser instanciadas por meio de lambdas. As expressões lambdas permitem tratar funcionalidades como argumento de método, ou código como dados. Abaixo estão alguns exemplos de lambdas. As entradas estão à esquerda e o código à direita. Os tipos de entrada podem ser inferidos e são opcionais:

  (int x, int y) -> { return x + y; }

  (x, y) -> x + y

  x -> x * x

  () -> x

  x -> { System.out.println(x); }

Aqui está um exemplo de instanciação da interface funcional Runnable:

  Runnable r = () -> { System.out.println("Running!"); }

 

Referências de métodos

As referências de métodos são expressões lambdas compactas para métodos que já possuem um nome. Aqui estão alguns exemplos de referências de métodos, com o seu equivalente em expressão lambda à direita:

  String::valueOf     x -> String.valueOf(x)
  Object::toString    x -> x.toString()
  x::toString         () -> x.toString()
  ArrayList::new      () -> new ArrayList<>()

 

Lambdas capturantes ou não capturantes

As lambdas são ditas como "capturantes" se acessarem uma variável não static ou objeto que estavam definidos fora do corpo da lambda. Por exemplo, a expressão lambda a seguir está acessando uma variável x:

  int x = 5;
  return y -> x + y;

Uma expressão lambda pode apenas acessar variáveis locais e parâmetros dentro de blocos que são final ou efetivamente final.

 

java.util.function

Um grande número de novas interfaces funcionais foi adicionado no pacote java.util.function. A seguir temos alguns exemplos:

  • Function<T, R> − recebe T como entrada, retorna R como saída;
  • Predicate<T> − recebe T como entrada, retorna um valor booleano como saída;
  • Consumer<T> − recebe T como entrada, não retorna nada como saída;
  • Supplier<T> − não recebe entrada, retorna T como saída;
  • BinaryOperator<T> − recebe duas entradas T, retorna um T como saída.

 

java.util.stream

O novo pacote java.util.stream fornece classes para apoiar operações no estilo funcional sobre os fluxos de dados. Uma maneira comum de obter um fluxo será por meio de uma coleção (collection):

  Stream<T> stream = collection.stream();

Aqui está um exemplo do pacote Javadocs:

  int sumOfWeights = blocks.stream().filter(b -> b.getColor() == RED)
                                    .mapToInt(b -> b.getWeight())
                                    .sum();

Aqui usamos um bloco de Collection como fonte de fluxo, e então otimizamos um filtro de redução de mapa (filter-map-reduce) no fluxo para obter a soma dos pesos dos blocos vermelhos.

Os fluxos podem ser infinitos e manter o estado (stateful), também podem ser sequenciais ou em paralelo. Para se trabalhar com fluxos, primeiro deve-se obter um de alguma fonte, realizar uma ou mais operações intermediarias, e então executar uma operação terminal. As operações intermediarias incluem filter, map, flatMap, peel, distinct, sorted, limit e substream.

As operações terminais incluem forEach, toArray, reduce, collect, min, max, count, anyMatch, allMatch, noneMatch, findFirst e findAny. Uma classe muito útil é a java.util.stream.Collectors que implementa várias operações de redução, tal como conversão de fluxos em coleções e elementos de agregação.

 

Melhoras na inferência dos tipos genéricos

Isso melhora a habilidade do compilador Java para inferir tipos genéricos e reduzir os argumentos de tipos informados nas chamadas dos métodos genéricos. No Java 7 o código é parecido com o exemplo a seguir:

  foo(Utility.<Type>bar());
  Utility.<Type>foo().bar();

No Java 8, foi aprimorada a inferência de argumentos e o encadeamento de chamadas permite escrever um código como este:

  foo(Utility.bar());
    Utility.foo().bar();

 

java.time

A nova API de data e hora está dentro do pacote java.time. Todas as classes são imutáveis e thread-safe. Os tipos de data e hora inclusos são: Instant, LocalDate, LocalTime, LocalDateTime e ZonedDateTime. Além das datas e horas, também existem os tipos Duration e Period. Para completar também foram incluídos os tipos Month, DayOfWeek, Year, Month, YearMonth, MonthDay, OffsetTime e OffsetDateTime. A maioria das novas classes de data e hora são suportadas pelo JDBC.

 

Complementando a API Collections

A habilidade das interfaces de terem métodos padrão permitiu que o Java 8 adicionasse uma grande quantidade de métodos novos na API Collections. As implementações padrões são fornecidas em todas as interfaces e implementações mais eficientes serão adicionadas às classes completas quando apropriado. A seguir temos uma lista dos novos métodos:

  • Iterable.forEach(Consumer)
  • Iterator.forEachRemaining(Consumer)
  • Collection.removeIf(Predicate)
  • Collection.spliterator()
  • Collection.stream()
  • Collection.parallelStream()
  • List.sort(Comparator)
  • List.replaceAll(UnaryOperator)
  • Map.forEach(BiConsumer)
  • Map.replaceAll(BiFunction)
  • Map.putIfAbsent(K, V)
  • Map.remove(Object, Object)
  • Map.replace(K, V, V)
  • Map.replace(K, V)
  • Map.computeIfAbsent(K, Function)
  • Map.computeIfPresent(K, BiFunction)
  • Map.compute(K, BiFunction)
  • Map.merge(K, V, BiFunction)
  • Map.getOrDefault(Object, V)

 

Complementando a API Concurrency

Houve algumas adições na API Concurrency, algumas das quais serão discutidas brevemente aqui. O ForkJoinPool.commonPool() é uma estrutura que trata as operações de fluxo em paralelo. A fila comum é utilizada por qualquer ForkJoinTask que não apresentar explicitamente uma fila especifica. A ConcurrentHashMap está sendo reescrita complemente. A StampedLock é a nova implementação de bloqueio que pode ser utilizada como uma alternativa para o ReentrantReadWriteLock. A CompletableFuture é uma implementação da interface Future que fornece métodos para executar e encadear tarefas assíncronas.

 

Complementando a API IO/NIO

Há novos métodos de IO/NIO, que são utilizados para obter um java.util.stream.Stream de arquivos ou fluxos de entrada:

  • BufferedReader.lines()
  • Files.list(Path)
  • Files.walk(Path, int, FileVisitOption...)
  • Files.walk(Path, FileVisitOption...)
  • Files.find(Path, int, BiPredicate, FileVisitOption...)
  • Files.lines(Path, Charset)
  • DirectoryStream.stream()

Há uma nova UncheckedIOException, que é uma IOException que estende a RuntimeException. Há também o CloseableStream, um stream que pode e deve ser fechado.

 

Mudanças no Reflection e nas anotações

Com os anotações de tipos, as anotações poderão ser escritas em mais locais, como um argumento de tipos genéricos como List<@Nullable String>. Isso aprimora a detecção de erros pelas ferramentas de análise estáticas o que fortalecerá e refinará o sistema de tipos embarcados no Java.

 

Nashorn: mecanismo de JavaScript

O Nashorn é a implementação mais nova, leve e de alto desempenho de JavaScript integrado no JDK. O Nashorn é o sucessor do Rhino com desempenho aprimorado e melhor uso de memória. Ele contará com a API javax.script, mas não incluirá o suporte a DOM/CSS e também não incluirá API de plugins para navegadores.

 

Outros complementos adicionados no java.lang, java.util e em outros locais.

Há outros complementos adicionados em outros pacotes que não foram mencionados. Aqui estão alguns dos mais notáveis: A ThreadLocal.withInitial(Supplier) permite a declaração de uma variável thread-local mais compacta. As muito atrasadas StringJoiner e String.join(...) são finalmente parte do Java 8. O Comparator oferece alguns métodos novos para executar o encadeamento e comparação baseada em campos (atributos). O tamanho do pool padrão do mapa de String está maior, por volta de 25 a 50K.

Para mais informações visite o post Tudo sobre Java 8. A última atualização desse post foi dia 10 de Setembro de 2013.

Avalie esse artigo

Relevância
Estilo/Redação

Conteúdo educacional

BT