O Java 11 não apresentou nenhum recurso inovador, mas contém várias gemas das quais você talvez ainda não tenha ouvido falar. Já olhou o mais recente em String , Optional , Collection e outros cavalos de trabalho? Caso contrário, você chegou ao endereço: hoje veremos 11 jóias escondidas do Java 11!
Inferência de tipo para parâmetros lambda
Ao escrever uma expressão lambda, você pode escolher entre especificar tipos explicitamente e ignorá-los:
 Function<String, String> append = string -> string + " "; Function<String, String> append = (String s) -> s + " "; 
O Java 10 apresentou var , mas não pôde ser usado em lambdas:
 
No Java 11 já é possível. Mas porque? Não parece que var deu mais do que apenas um passe de tipo. Embora este seja o caso, o uso de var tem duas vantagens menores:
- torna o uso de varmais universal, removendo a exceção à regra
- permite adicionar anotações ao tipo de parâmetro sem recorrer ao uso de seu nome completo
Aqui está um exemplo do segundo caso:
 List<EnterpriseGradeType<With, Generics>> types = ; types .stream()  
Embora a mistura de tipos derivados, explícitos e implícitos em expressões lambda da forma (var type, String option, index) -> ... possa ser implementada, mas ( na estrutura do JEP-323 ) este trabalho não foi realizado. Portanto, é necessário escolher uma das três abordagens e aderir a ela para todos os parâmetros da expressão lambda. A necessidade de especificar var para todos os parâmetros para adicionar anotação para um deles pode ser um pouco irritante, mas geralmente suportável.
Processamento de fluxo de strings com 'String::lines'
Tem uma sequência de várias linhas? Deseja fazer algo com cada linha? Então String::lines é a escolha certa:
 var multiline = "\r\n\r\n\r\n"; multiline .lines()  
Observe que a linha original usa os delimitadores \r\n Windows e, embora eu esteja no Linux, lines() ainda a quebrou. Isso se deve ao fato de que, apesar do sistema operacional atual, esse método interpreta \r , \n e \r\n como quebras de linha - mesmo se elas estiverem misturadas na mesma linha.
Um fluxo de linhas nunca contém os próprios separadores de linhas. As linhas podem estar vazias ( "\n\n \n\n" , que contém 5 linhas), mas a última linha da linha original será ignorada se estiver vazia ( "\n\n" ; 2 linhas). (Observação pelo tradutor: é conveniente que eles tenham line , mas que tenham string , e nós temos as duas.)
Diferente da split("\R") , as lines() preguiçosas e, cito , "fornecem melhor desempenho [...] procurando mais rapidamente novas quebras de linha". (Se alguém desejar registrar uma referência no JMH para verificação, informe-me). Também reflete melhor o algoritmo de processamento e usa uma estrutura de dados mais conveniente (fluxo em vez de matriz).
Removendo o espaço em branco com 'String::strip' etc.
Inicialmente, String tinha um método de remoção para remover espaços em branco, que era considerado tudo com códigos até U+0020 . Sim, BACKSPACE ( U+0008) é um espaço em branco como BELL ( U+0007 ), mas o LINE SEPARATOR ( U+2028 ) não é mais considerado como tal.
O Java 11 introduziu o método strip , cuja abordagem tem mais nuances. Ele usa o Character::isWhitespace do Java 5 para determinar o que exatamente precisa ser removido. A partir de sua documentação , é claro que isso:
- SPACE SEPARATOR,- SPACE SEPARATOR- LINE SEPARATOR,- LINE SEPARATOR- PARAGRAPH SEPARATOR, mas não um espaço inextricável
- HORIZONTAL TABULATION(- U+0009),- LINE FEED(- U+000A),- VERTICAL TABULATION(- U+000B),- FORM FEED(- U+000C),- CARRIAGE RETURN(- U+000D)
- FILE SEPARATOR(- U+001C),- GROUP SEPARATOR(- U+001D),- RECORD SEPARATOR(- U+001E),- UNIT SEPARATOR(- U+001F)
Com a mesma lógica, existem mais dois métodos de limpeza, stripLeading e stripTailing , que fazem exatamente o que é esperado deles.
E, finalmente, se você só precisa descobrir se a linha fica vazia depois de remover o espaço em branco, não há realmente necessidade de excluí-la - basta usar isBlank :
 " ".isBlank();  
Repetindo strings com 'String::repeat'
Capte a ideia:
Etapa 1: Vigiando o JDK

Etapa 2: Localizando perguntas relacionadas ao StackOverflow

Etapa 3: chegando com uma nova resposta com base em alterações futuras

Etapa 4: ????
Etapa 4: Lucro

Como você pode imaginar, String possui um novo método repeat(int) . Funciona exatamente de acordo com as expectativas e há pouco a discutir.
Criando caminhos com 'Path::of'
Eu realmente gosto da API do Path , mas a conversão de caminhos entre diferentes modos de exibição (como Path , File , URL , URI e String ) ainda é irritante. Este ponto se tornou menos confuso no Java 11, copiando dois métodos Paths::get para Path::of methods:
 Path tmp = Path.of("/home/nipa", "tmp"); Path codefx = Path.of(URI.create("http://codefx.org")); 
Eles podem ser considerados canônicos, pois os dois métodos antigos Paths::get usam novas opções.
Lendo e gravando arquivos com 'Files::readString' e 'Files::writeString'
Se eu precisar ler de um arquivo grande, normalmente uso o Files::lines para obter um fluxo lento de suas linhas. Da mesma forma, para registrar uma grande quantidade de dados que não podem ser armazenados inteiramente na memória, eu uso o Files::write passando-os como Iterable<String> .
Mas e o caso simples quando quero processar o conteúdo de um arquivo como uma única linha? Isso não é muito conveniente, pois o Files::readAllBytes e as variantes apropriadas do Files::write operam em matrizes de bytes.
E, em seguida, o Java 11 aparece, adicionando readString e writeString aos Files :
 String haiku = Files.readString(Path.of("haiku.txt")); String modified = modify(haiku); Files.writeString(Path.of("haiku-mod.txt"), modified); 
Claro e fácil de usar. Se necessário, você pode passar o conjunto de readString para readString e, em writeString também uma matriz OpenOptions .
E / S vazia com 'Reader::nullReader' , etc.
Precisa de um OutputStream que não OutputStream lugar algum? Ou um InputStream vazio? E o Reader e o Writer que não fazem nada? O Java 11 tem tudo:
 InputStream input = InputStream.nullInputStream(); OutputStream output = OutputStream.nullOutputStream(); Reader reader = Reader.nullReader(); Writer writer = Writer.nullWriter(); 
(Nota do tradutor: no commons-io do Apache, essas classes existem desde 2014).
No entanto, estou surpreso - é null realmente o melhor prefixo? Não gosto de como é usado para significar "ausência intencional" ... Talvez seja melhor usar noOp ? (Nota do tradutor: provavelmente esse prefixo foi escolhido devido ao uso comum de /dev/null .)
{ } ~> [ ] com 'Collection::toArray'
Como você converte coleções em matrizes?
 
A primeira opção, objects , perde todas as informações sobre os tipos e, portanto, está em andamento. E o resto? Ambos são volumosos, mas o primeiro é mais curto. O último cria uma matriz do tamanho necessário, para que pareça mais produtivo (ou seja, "parece mais produtivo", veja credibilidade ). Mas é realmente mais produtivo? Não, pelo contrário, é mais lento (no momento).
Mas por que eu deveria me importar com isso? Não existe uma maneira melhor de fazer isso? No Java 11, existem:
 String[] strings_fun = list.toArray(String[]::new); 
Uma nova variante de Collection::toArray , que aceita IntFunction<T[]> , ou seja, uma função que recebe o tamanho da matriz e retorna uma matriz do tamanho necessário. Ele pode ser expresso brevemente como uma referência a um construtor do formato T[]::new (para um T bem conhecido).
Fato interessante: a implementação padrão da Collection#toArray(IntFunction<T[]>) sempre passa 0 para o gerador de array. Inicialmente, decidi que essa solução era baseada no melhor desempenho para matrizes de comprimento zero, mas agora acho que o motivo pode ser que, para algumas coleções, calcular o tamanho possa ser uma operação muito cara e você não deva usar essa abordagem na implementação padrão da Collection . No entanto, implementações de coleções específicas, como ArrayList , podem alterar essa abordagem, mas não são alteradas no Java 11. Não vale a pena, eu acho.
Verificação de ausência com 'Optional::isEmpty'
Com o uso abundante do Optional , especialmente em grandes projetos, onde você frequentemente encontra uma abordagem não Optional , é necessário verificar se ele tem um valor. Existe um método Optional::isPresent para isso. Mas com a mesma frequência, você precisa saber o oposto - esse Optional vazio. Não tem problema, basta usar !opt.isPresent() , certo?
Obviamente, é possível assim, mas quase sempre é mais fácil entender a lógica if sua condição não for invertida. E, às vezes, o Optional aparece no final de uma longa cadeia de chamadas e, se você precisar checar por nada, então precisa apostar ! no começo:
 public boolean needsToCompleteAddress(User user) { return !getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isPresent(); } 
Nesse caso, pule ! muito facil A partir do Java 11, há uma opção melhor:
 public boolean needsToCompleteAddress(User user) { return getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isEmpty(); } 
Invertendo predicados com 'Predicate::not'
Falando em inverter ... A interface do Predicate possui um negate instância negate : retorna um novo predicado que executa a mesma verificação, mas inverte o resultado. Infelizmente, eu raramente consigo usá-lo ...
 
O problema é que raramente tenho acesso à instância do Predicate . Mais frequentemente, quero obter essa instância por meio de um link para um método (e invertê-lo), mas, para que isso funcione, o compilador deve saber o que levar a referência ao método - sem ele, ele não pode fazer nada. E é exatamente isso que acontece se você usar a construção (String::isBlank).negate() : o compilador não sabe mais o que String::isBlank deve estar nisso e desiste. Uma casta especificada corretamente corrige isso, mas a que custo?
Embora exista uma solução simples. Não use o negate instância negate , mas use o novo método estático Predicate.not(Predicate<T>) do Java 11:
 Stream .of("a", "b", "", "c")  
Já está melhor!
Expressões regulares como um predicado com 'Pattern::asMatchPredicate'
Existe uma expressão regular? Precisa filtrar dados nele? Que tal isso:
 Pattern nonWordCharacter = Pattern.compile("\\W"); Stream .of("Metallica", "Motörhead") .filter(nonWordCharacter.asPredicate()) .forEach(System.out::println); 
Fiquei muito feliz em encontrar esse método! Vale a pena acrescentar que este é um método do Java 8. Opa, eu perdi então. O Java 11 adicionou outro método semelhante: Pattern::asMatchPredicate . Qual a diferença?
- asPredicateverifica se a sequência ou parte da sequência corresponde ao padrão (funciona como- s -> this.matcher(s).find())
- asMatchPredicateverifica se a sequência inteira corresponde ao padrão (funciona como- s -> this.matcher(s).matches())
Por exemplo, temos uma expressão regular que verifica os números de telefone, mas não contém ^ e $ para rastrear o início e o fim de uma linha. Em seguida, o código a seguir não funcionará conforme o esperado:
 prospectivePhoneNumbers .stream() .filter(phoneNumberPatter.asPredicate()) .forEach(this::robocall); 
Você percebeu um erro? Uma linha como " -152 ? +1-202-456-1414" será filtrada porque contém um número de telefone válido. Por outro lado, Pattern::asMatchPredicate não permitirá isso, porque a cadeia inteira não corresponderá mais ao padrão.
Auto-teste
Aqui está uma visão geral de todas as onze pérolas - você ainda se lembra do que cada método faz? Nesse caso, você passou no teste.
- em String:
 - Stream<String> lines()
- String strip()
- String stripLeading()
- String stripTrailing()
- boolean isBlank()
- String repeat(int)
 
- no Path:
 - static Path of(String, String...)
- static Path of(URI)
 
- em Files:
 - String readString(Path) throws IOException
- Path writeString(Path, CharSequence, OpenOption...) throws IOException
- Path writeString(Path, CharSequence, Charset, OpenOption...) throws IOException
 
- em InputStream:static InputStream nullInputStream()
- em OutputStream:OutputStreamstatic OutputStream nullOutputStream()
- no Reader:Readerstatic Reader nullReader()
- no Writer:static Writer nullWriter()
- na Collection:T[] toArray(IntFunction<T[]>)
- em Optional:boolean isEmpty()
- no Predicate:Predicatestatic Predicate<T> not(Predicate<T>)
- em Pattern:Predicate<String> asMatchPredicate()
Divirta-se com o Java 11!