Java 11 no introdujo ninguna característica innovadora, pero contiene varias gemas de las que quizás aún no haya oído hablar. ¿Ya viste lo último en String
, Optional
, Collection
y otros caballos de batalla? Si no, ha llegado a la dirección: ¡hoy veremos 11 gemas ocultas de Java 11!
Inferencia de tipos para parámetros lambda
Al escribir una expresión lambda, puede elegir entre especificar explícitamente los tipos y omitirlos:
Function<String, String> append = string -> string + " "; Function<String, String> append = (String s) -> s + " ";
Java 10 introdujo var
, pero no se pudo usar en lambdas:
En Java 11 ya es posible. Pero por que? No parece que var
dado más que un simple pase de tipo. Aunque este es el caso, el uso de var
tiene dos ventajas menores:
- hace que el uso de
var
más universal al eliminar la excepción a la regla - le permite agregar anotaciones al tipo de parámetro sin recurrir al uso de su nombre completo
Aquí hay un ejemplo del segundo caso:
List<EnterpriseGradeType<With, Generics>> types = ; types .stream()
Aunque se puede implementar la mezcla de tipos derivados, explícitos e implícitos en expresiones lambda de la forma (var type, String option, index) -> ...
, pero ( en el marco de JEP-323 ) este trabajo no se llevó a cabo. Por lo tanto, es necesario elegir uno de los tres enfoques y adherirse a él para todos los parámetros de la expresión lambda. La necesidad de especificar var
para todos los parámetros para agregar anotaciones para uno de ellos puede ser un poco molesto, pero generalmente tolerable.
Procesamiento continuo de cadenas con 'String::lines'
¿Tienes una cadena de varias líneas? ¿Quieres hacer algo con cada línea? Entonces String::lines
es la elección correcta:
var multiline = "\r\n\r\n\r\n"; multiline .lines()
Tenga en cuenta que la línea original usa los delimitadores de tornillo \r\n
y aunque estoy en Linux, las lines()
todavía lo rompieron. Esto se debe al hecho de que, a pesar del sistema operativo actual, este método interpreta \r
, \n
y \r\n
como saltos de línea, incluso si se mezclan en la misma línea.
Una secuencia de líneas nunca contiene los propios separadores de línea. Las líneas pueden estar vacías ( "\n\n \n\n"
, que contiene 5 líneas), pero la última línea de la línea original se ignora si está vacía ( "\n\n"
; 2 líneas). (Nota del traductor: es conveniente para ellos tener line
, pero tener string
, y tenemos ambos).
A diferencia de la split("\R")
, las lines()
vagas y, cito , "proporciona un mejor rendimiento [...] al buscar más rápidamente nuevos saltos de línea". (Si alguien quiere presentar un punto de referencia en JMH para verificación, hágamelo saber). También refleja mejor el algoritmo de procesamiento y utiliza una estructura de datos más conveniente (flujo en lugar de matriz).
Eliminando espacios en blanco con 'String::strip'
, etc.
Inicialmente, String
tenía un método de trim
para eliminar espacios en blanco, que se consideraba todo con códigos hasta U+0020
. Sí, BACKSPACE
( U+0008)
es un espacio en blanco como BELL
( U+0007
), pero LINE SEPARATOR
( U+2028
) ya no se considera como tal.
Java 11 introdujo el método de la strip
, cuyo enfoque tiene más matices. Utiliza el Character::isWhitespace
de Java 5 para determinar qué necesita eliminarse exactamente. De su documentación queda claro que esto:
SPACE SEPARATOR
LINE SEPARATOR
SPACE SEPARATOR
, LINE SEPARATOR
, LINE SEPARATOR
PARAGRAPH SEPARATOR
, pero no es un espacio inextricableHORIZONTAL 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
)
Con la misma lógica, hay dos métodos de limpieza más, stripLeading
y stripTailing
, que hacen exactamente lo que se espera de ellos.
Y finalmente, si solo necesita averiguar si la línea se vacía después de eliminar el espacio en blanco, entonces no hay necesidad de eliminarlos realmente, solo use isBlank
:
" ".isBlank();
Repetir cadenas con 'String::repeat'
Captura la idea:
Paso 1: Vigilar JDK

Paso 2: Búsqueda de preguntas relacionadas con StackOverflow

Paso 3: llegar con una nueva respuesta basada en cambios futuros

Paso 4: ????
Paso 4: ganancia

Como puedes imaginar, String
tiene un nuevo método de repeat(int)
. Funciona exactamente de acuerdo con las expectativas, y hay poco que discutir.
Crear rutas con 'Path::of'
Realmente me gusta la API de Path
, pero convertir rutas entre diferentes vistas (como Path
, File
, URL
, URI
y String
) sigue siendo molesto. Este punto se ha vuelto menos confuso en Java 11 copiando dos Paths::get
method en Path::of
method:
Path tmp = Path.of("/home/nipa", "tmp"); Path codefx = Path.of(URI.create("http://codefx.org"));
Pueden considerarse canónicos, ya que los dos métodos antiguos de Paths::get
utilizan nuevas opciones.
Lectura y escritura de archivos con 'Files::readString'
y 'Files::writeString'
Si necesito leer de un archivo grande, generalmente uso Files::lines
para obtener un flujo lento de sus líneas. Del mismo modo, para escribir una gran cantidad de datos que pueden no almacenarse en la memoria en su totalidad, utilizo Files::write
pasándolos como Iterable<String>
.
Pero, ¿qué pasa con el caso simple cuando quiero procesar el contenido de un archivo como una sola línea? Esto no es muy conveniente, ya que Files::readAllBytes
y las variantes apropiadas de Files::write
operan en conjuntos de bytes.
Y luego aparece Java 11, agregando readString
y writeString
a los Files
:
String haiku = Files.readString(Path.of("haiku.txt")); String modified = modify(haiku); Files.writeString(Path.of("haiku-mod.txt"), modified);
Claro y fácil de usar. Si es necesario, puede pasar el Charset
de readString
a readString
, y en writeString
también una matriz OpenOptions
.
E / S vacía con 'Reader::nullReader'
, etc.
¿Necesita un OutputStream
que no escriba en ningún lado? ¿O un InputStream
vacío? ¿Qué pasa con Reader
y Writer
que no hacen nada? Java 11 lo tiene todo:
InputStream input = InputStream.nullInputStream(); OutputStream output = OutputStream.nullOutputStream(); Reader reader = Reader.nullReader(); Writer writer = Writer.nullWriter();
(Nota del traductor: en commons-io
estas clases existen desde aproximadamente 2014).
Sin embargo, me sorprende: ¿es null
realmente el mejor prefijo? No me gusta cómo se usa para significar "ausencia intencional" ... ¿Quizás sería mejor usar noOp
? (Nota del traductor: lo más probable es que este prefijo se haya elegido debido al uso común de /dev/null
).
{ } ~> [ ]
con 'Collection::toArray'
¿Cómo se convierten las colecciones en matrices?
La primera opción, objects
, pierde toda la información sobre los tipos, por lo que está en fuga. ¿Qué hay del resto? Ambos son voluminosos, pero el primero es más corto. Este último crea una matriz del tamaño requerido, para que se vea más productivo (es decir, "parece más productivo", ver credibilidad ). ¿Pero es realmente más productivo? No, por el contrario, es más lento (por el momento).
Pero, ¿por qué debería importarme esto? ¿No hay una mejor manera de hacer esto? En Java 11 hay:
String[] strings_fun = list.toArray(String[]::new);
Collection::toArray
una nueva variante de Collection::toArray
, que acepta IntFunction<T[]>
, es decir una función que recibe el tamaño de la matriz y devuelve una matriz del tamaño requerido. Se puede expresar brevemente como una referencia a un constructor de la forma T[]::new
(para un T
conocido).
Dato interesante, la implementación predeterminada de Collection#toArray(IntFunction<T[]>)
siempre pasa 0
al generador de matriz. Al principio, decidí que esta solución se basaba en el mejor rendimiento para matrices de longitud cero, pero ahora creo que la razón puede ser que para algunas colecciones, calcular el tamaño puede ser una operación muy costosa y no debe usar este enfoque en la implementación predeterminada de Collection
. Sin embargo, las implementaciones de colecciones específicas, como ArrayList
, pueden cambiar este enfoque, pero no cambian en Java 11. No vale la pena, supongo.
Verificación de ausencia con 'Optional::isEmpty'
Con el uso abundante de Optional
, especialmente en proyectos grandes, donde a menudo encuentra un enfoque no Optional
, a menudo tiene que verificar si tiene un valor. Hay un método Optional::isPresent
para esto. Pero con la misma frecuencia necesita saber lo contrario: que Optional
vacío. No hay problema, solo use !opt.isPresent()
, ¿verdad?
Por supuesto, es posible de esa manera, pero casi siempre es más fácil entender la lógica if
su condición no se invierte. Y a veces, Optional
aparece al final de una larga cadena de llamadas y si necesita verificarlo para nada, ¡entonces tiene que apostar !
al principio
public boolean needsToCompleteAddress(User user) { return !getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isPresent(); }
En ese caso, ¡sáltatelo !
Muy fácil Comenzando con Java 11 hay una mejor opción:
public boolean needsToCompleteAddress(User user) { return getAddressRepository() .findAddressFor(user) .map(this::canonicalize) .filter(Address::isComplete) .isEmpty(); }
Invertir predicados con 'Predicate::not'
Hablando de invertir ... La interfaz de Predicate
tiene un negate
instancia negate
: devuelve un nuevo predicado que realiza la misma verificación, pero invierte su resultado. Desafortunadamente, rara vez logro usarlo ...
El problema es que rara vez tengo acceso a la instancia de Predicate
. Más a menudo, quiero obtener una instancia de este tipo a través de un enlace a un método (e invertirlo), pero para que esto funcione, el compilador debe saber a qué llevar la referencia del método; sin él, no puede hacer nada. Y esto es exactamente lo que sucede si usa la (String::isBlank).negate()
: el compilador ya no sabe qué String::isBlank
debería estar en esto y se da por vencido. Una casta correctamente especificada arregla esto, pero ¿a qué costo?
Aunque hay una solución simple. No use el negate
instancia negate
, pero use el nuevo método estático Predicate.not(Predicate<T>)
de Java 11:
Stream .of("a", "b", "", "c")
Ya mejor!
Expresiones regulares como predicado con 'Pattern::asMatchPredicate'
¿Hay una expresión regular? ¿Necesita filtrar datos en él? ¿Qué tal esto?
Pattern nonWordCharacter = Pattern.compile("\\W"); Stream .of("Metallica", "Motörhead") .filter(nonWordCharacter.asPredicate()) .forEach(System.out::println);
¡Estaba muy feliz de encontrar este método! Vale la pena agregar que este es un método de Java 8. Vaya, lo perdí entonces. Java 11 agregó otro método similar: Pattern::asMatchPredicate
. Cual es la diferencia
asPredicate
comprueba que la cadena o parte de la cadena coincida con el patrón (funciona como s -> this.matcher(s).find()
)asMatchPredicate
verifica que toda la cadena coincida con el patrón (funciona como s -> this.matcher(s).matches()
)
Por ejemplo, tenemos una expresión regular que verifica los números de teléfono, pero no contiene ^
y $
para rastrear el principio y el final de una línea. Entonces el siguiente código no funcionará como es de esperar:
prospectivePhoneNumbers .stream() .filter(phoneNumberPatter.asPredicate()) .forEach(this::robocall);
¿Notaste un error? Se " -152 ? +1-202-456-1414"
una línea como " -152 ? +1-202-456-1414"
, porque contiene un número de teléfono válido. Por otro lado, Pattern::asMatchPredicate
no permitirá esto, porque toda la cadena ya no coincidirá con el patrón.
Auto prueba
Aquí hay una descripción general de las once perlas: ¿todavía recuerda lo que hace cada método? Si es así, has pasado la prueba.
- en
String
:
Stream<String> lines()
String strip()
String stripLeading()
String stripTrailing()
boolean isBlank()
String repeat(int)
- en el
Path
:
static Path of(String, String...)
static Path of(URI)
- en
Files
:
String readString(Path) throws IOException
Path writeString(Path, CharSequence, OpenOption...) throws IOException
Path writeString(Path, CharSequence, Charset, OpenOption...) throws IOException
- en
InputStream
: static InputStream nullInputStream()
- en
OutputStream
: static OutputStream nullOutputStream()
- en el
Reader
: Reader
static Reader nullReader()
- en
Writer
: Writer
static Writer nullWriter()
- en la
Collection
: T[] toArray(IntFunction<T[]>)
- en
Optional
: boolean isEmpty()
- en
Predicate
: Predicate
static Predicate<T> not(Predicate<T>)
- en
Pattern
: Predicate<String> asMatchPredicate()
¡Diviértete con Java 11!