Novos recursos no Java 8

Novos recursos no Java 8

1. Visão geral

Neste artigo, daremos uma olhada rápida em alguns dos novos recursos mais interessantes do Java 8.

Falaremos sobre: ​​métodos de interface padrão e estáticos, referência de método e opcional.

Já cobrimos alguns dos recursos do lançamento do Java 8 -stream API,lambda expressions and functional interfaces - pois são tópicos abrangentes que merecem uma análise separada.

2. Métodos estáticos e padrão de interface

Antes do Java 8, as interfaces podiam ter apenas métodos abstratos públicos. Não foi possível adicionar novas funcionalidades à interface existente sem forçar todas as classes de implementação a criar uma implementação dos novos métodos, nem foi possível criar métodos de interface com uma implementação.

A partir do Java 8, as interfaces podem ter métodosstaticedefault que, apesar de serem declarados em uma interface, possuem um comportamento definido.

2.1. Método estático

Considere o seguinte método da interface (vamos chamar essa interfaceVehicle):

static String producer() {
    return "N&F Vehicles";
}

O métodoproducer() estático está disponível apenas por meio e dentro de uma interface. Não pode ser substituído por uma classe de implementação.

Para chamá-lo fora da interface, a abordagem padrão para chamada de método estático deve ser usada:

String producer = Vehicle.producer();

2.2. Método Padrão

Os métodos padrão são declarados usando o novodefault keyword. Eles são acessíveis através da instância da classe de implementação e podem ser substituídos.

Vamos adicionar um métododefault à nossa interfaceVehicle, que também fará uma chamada para o métodostatic desta interface:

default String getOverview() {
    return "ATV made by " + producer();
}

Suponha que esta interface seja implementada pela classeVehicleImpl.. Para executar o métododefault, uma instância desta classe deve ser criada:

Vehicle vehicle = new VehicleImpl();
String overview = vehicle.getOverview();

3. Referências de método

A referência de método pode ser usada como uma alternativa mais curta e legível para uma expressão lambda que chama apenas um método existente. Existem quatro variantes de referências de método.

3.1. Referência a um método estático

A referência a um método estático contém a seguinte sintaxe:ContainingClass::methodName.

Vamos tentar contar todas as strings vazias emList<String> com a ajuda da API Stream.

boolean isReal = list.stream().anyMatch(u -> User.isRealUser(u));

Dê uma olhada mais de perto na expressão lambda no métodoanyMatch(), ele apenas faz uma chamada para um método estáticoisRealUser(User user) da classeUser. Portanto, ele pode ser substituído por uma referência a um método estático:

boolean isReal = list.stream().anyMatch(User::isRealUser);

Esse tipo de código parece muito mais informativo.

3.2. Referência a um método de instância

A referência a um método de instância contém a seguinte sintaxe:containingInstance::methodName. O código a seguir chama o métodoisLegalName(String string) do tipoUser que valida um parâmetro de entrada:

User user = new User();
boolean isLegalName = list.stream().anyMatch(user::isLegalName);

3.3. Referência a um método de instância de um objeto de um tipo específico

Este método de referência usa a seguinte sintaxe:ContainingType::methodName. Um exemplo
long count = list.stream().filter(String::isEmpty).count();

3.4. Referência a um construtor

Uma referência a um construtor assume a seguinte sintaxe:ClassName::new. Como o construtor em Java é um método especial, a referência de método também pode ser aplicada a ele com a ajuda denew como nome de método.

Stream stream = list.stream().map(User::new);

4. opcional

Antes, os desenvolvedores Java 8 tinham que validar cuidadosamente os valores aos quais se referiam, devido à possibilidade de lançarNullPointerException (NPE). Todas essas verificações exigiam um código clichê bastante irritante e propenso a erros.

A classe Java 8Optional<T> pode ajudar a lidar com situações em que existe a possibilidade de obter oNPE.. Funciona como um contêiner para o objeto do tipoT.. Pode retornar um valor deste objeto se este valor não énull. Quando o valor dentro deste contêiner énull, ele permite fazer algumas ações predefinidas em vez de lançarNPE.

4.1. Criação doOptional<T>

Uma instância da classeOptional pode ser criada com a ajuda de seus métodos estáticos:

Optional optional = Optional.empty();

Retorna umOptional. vazio

String str = "value";
Optional optional = Optional.of(str);

Retorna umOptional que contém um valor não nulo.

Optional optional = Optional.ofNullable(getString());

RetornaráOptional com um valor específico ouOptional vazio se o parâmetro fornull.

4.2. Optional<T> usage

Por exemplo, você espera obter umList<String>e, no caso denull, deseja substituí-lo por uma nova instância de umArrayList<String>.W com código pré-Java 8, precisa fazer algo assim:

List list = getList();
List listOpt = list != null ? list : new ArrayList<>();

Com o Java 8, a mesma funcionalidade pode ser alcançada com um código muito mais curto:

List listOpt = getList().orElseGet(() -> new ArrayList<>());

Existe ainda mais código clichê quando você precisa alcançar o campo de algum objeto da maneira antiga. Suponha que você tenha um objeto do tipoUser que possui um campo do tipoAddress com um campo street do tipoString. E por algum motivo você precisa retornar um valor de o campostreet se algum existir ou um valor padrão sestreet fornull:

User user = getUser();
if (user != null) {
    Address address = user.getAddress();
    if (address != null) {
        String street = address.getStreet();
        if (street != null) {
            return street;
        }
    }
}
return "not specified";

Isso pode ser simplificado comOptional:

Optional user = Optional.ofNullable(getUser());
String result = user
  .map(User::getAddress)
  .map(Address::getStreet)
  .orElse("not specified");

Neste exemplo, usamos o métodomap() para converter os resultados da chamada degetAdress() paraOptional<Address>egetStreet() paraOptional<String>. Se algum desses métodos retornounull o métodomap() retornaria umOptional. vazio

Imagine que nossos getters retornemOptional<T>. Então, devemos usar o métodoflatMap() em vez domap():

Optional optionalUser = Optional.ofNullable(getOptionalUser());
String result = optionalUser
  .flatMap(OptionalUser::getAddress)
  .flatMap(OptionalAddress::getStreet)
  .orElse("not specified");

Outro caso de uso deOptional está mudandoNPE com outra exceção. Então, como fizemos anteriormente, vamos tentar fazer isso no estilo pré-Java 8:

String value = null;
String result = "";
try {
    result = value.toUpperCase();
} catch (NullPointerException exception) {
    throw new CustomException();
}

E se usarmosOptional<String>? A resposta é mais legível e mais simples:

String value = null;
Optional valueOpt = Optional.ofNullable(value);
String result = valueOpt.orElseThrow(CustomException::new).toUpperCase();

Observe que como e com que propósito usarOptional em seu aplicativo é uma decisão de design séria e controversa, e a explicação de todos os seus prós e contras está fora do escopo deste artigo. Se você estiver interessado, pode aprofundar-se, há muitos artigos interessantes na Internet dedicados a esse problema. This oneethis another one podem ser muito úteis.

5. Conclusão

Neste artigo, discutimos brevemente alguns novos recursos interessantes no Java 8.

Obviamente, existem muitas outras adições e melhorias espalhadas por muitos pacotes e classes do Java 8 JDK.

Porém, as informações ilustradas neste artigo são um bom ponto de partida para explorar e aprender sobre alguns desses novos recursos.

Finalmente, todo o código fonte do artigo está disponívelover on GitHub.