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.