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
var
mais 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ávelHORIZONTAL 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?
asPredicate
verifica se a sequência ou parte da sequência corresponde ao padrão (funciona como s -> this.matcher(s).find()
)asMatchPredicate
verifica 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
: OutputStream
static OutputStream nullOutputStream()
- no
Reader
: Reader
static Reader nullReader()
- no
Writer
: static Writer nullWriter()
- na
Collection
: T[] toArray(IntFunction<T[]>)
- em
Optional
: boolean isEmpty()
- no
Predicate
: Predicate
static Predicate<T> not(Predicate<T>)
- em
Pattern
: Predicate<String> asMatchPredicate()
Divirta-se com o Java 11!