jdk - install java linux mint




En Java 8, ¿por qué las matrices no recibieron el método de cada uno de Iterable? (2)

Debo extrañar algo aquí.

En Java 5, se introdujo el enunciado "for-each loop" (también llamado el bucle forzado) . Parece que se introdujo principalmente para iterar a través de Colecciones . Cualquier clase de colección (o contenedor) que implemente la interfaz Iterable es elegible para iteración usando el "bucle for-each". Tal vez por razones históricas, las matrices de Java no implementaron la interfaz Iterable. Pero dado que las matrices eran omnipresentes, javac aceptaría el uso de cada bucle en las matrices (generando un bytecode equivalente a un bucle for tradicional).

En Java 8, el método forEach se agregó a la interfaz Iterable como método predeterminado . Esto hizo posible el paso de expresiones lambda a colecciones (mientras itera) posibles (por ejemplo, list.forEach(System.out::println) ). Pero, de nuevo, las matrices no disfrutan de este tratamiento. (Entiendo que hay soluciones).

¿Existen razones técnicas por las cuales javac no se pudo mejorar para aceptar matrices en forEach, al igual que las acepta en el bucle for mejorado? Parece que la generación de código sería posible sin requerir que las matrices implementen Iterable . ¿Estoy siendo ingenuo?

Esto es especialmente importante para un recién llegado al idioma que, de forma natural, utiliza matrices debido a su facilidad sintáctica. No es natural cambiar a Listas y usar Arrays.asList(1, 2, 3) .


Hay un montón de casos especiales en el lenguaje Java y en la JVM para matrices. Las matrices tienen una API, pero apenas es visible. Es como si las matrices se declararan tener:

  • implements Cloneable, Serializable
  • public final int length
  • public T[] clone() donde T es el tipo de componente de la matriz

Sin embargo, estas declaraciones no son visibles en ningún código fuente en ninguna parte. Consulte JLS 4.10.3 y JLS 10.7 para obtener explicaciones. Cloneable y Serializable son visibles a través de la reflexión, y son devueltos por una llamada a

Object[].class.getInterfaces()

Quizás sorprendentemente, el campo de length y el método clone() no son visibles de manera reflexiva. El campo de length no es un campo en absoluto; usarlo se convierte en un bytecode de arraylength especial. Una llamada a clone() da como resultado una llamada a un método virtual real, pero si el receptor es un tipo de matriz, esto es manejado especialmente por la JVM.

Notablemente, sin embargo, las clases de matriz no implementan la interfaz Iterable .

Cuando se agregó el bucle mejorado para "cada uno" en Java SE 5, admitió dos casos diferentes para la expresión del lado derecho: un tipo Iterable o un tipo de matriz ( JLS 14.14.2 ). La razón es que las instancias y arreglos Iterable se manejan de forma completamente diferente mediante la instrucción mejorada. Esa sección del JLS da el tratamiento completo, pero en términos más simples, la situación es la siguiente.

Para un Iterable<T> iterable , el código

for (T t : iterable) {
    <loop body>
}

es azúcar sintáctico para

for (Iterator<T> iterator = iterable.iterator(); iterator.hasNext(); ) {
    t = iterator.next();
    <loop body>
}

Para una matriz T[] , el código

for (T t : array) {
    <loop body>
}

es azúcar sintáctico para

int len = array.length;
for (int i = 0; i < len; i++) {
    t = array[i];
    <loop body>
}

Ahora, ¿por qué se hizo de esta manera? Sin duda sería posible que las matrices implementen Iterable , ya que ya implementan otras interfaces. También sería posible que el compilador sintetice una implementación Iterator respaldada por una matriz. (Hay un precedente para esto. El compilador ya sintetiza los métodos static values() y valueOf() que se agregan automáticamente a cada clase enum , como se describe en JLS 8.9.3 .)

Pero las matrices son una construcción de muy bajo nivel, y se espera que acceder a una matriz por un valor int sea ​​una operación extremadamente económica. Es bastante idiomático ejecutar un índice de bucle de 0 a la longitud de una matriz, incrementándose en uno cada vez. El bucle mejorado en una matriz hace exactamente eso. Si el bucle mejorado para una matriz se implementó utilizando el protocolo Iterable , creo que la mayoría de la gente se sorprendería desagradablemente al descubrir que el bucle en una matriz implicaba una llamada al método inicial y asignación de memoria (creando el Iterator ), seguido de dos llamadas al método por iteración de bucle

Entonces, cuando se agregaron los métodos predeterminados a Iterable en Java 8, esto no afectó las matrices.

Como han señalado otros, si tiene una matriz de tipo int , long , double o de referencia, es posible convertir esto en una secuencia usando una de las llamadas Arrays.stream() . Esto proporciona acceso a map() , filter() , forEach() , etc.

Sería bueno, sin embargo, si los casos especiales en el lenguaje Java y JVM para matrices fueran reemplazados por construcciones reales (junto con la reparación de un montón de otros problemas relacionados con matrices, como el manejo deficiente de matrices dimensionales de 2+, los 2 ^ 31 limitación de longitud, y así sucesivamente). Este es el tema de la investigación "Arrays 2.0" dirigida por John Rose. Vea la charla de John en JVMLS 2012 ( video , slides ). Las ideas relevantes para esta discusión incluyen la introducción de una interfaz real para matrices, para permitir que las bibliotecas interpongan el acceso a los elementos, para admitir operaciones adicionales tales como cortar y copiar, y así sucesivamente.

Tenga en cuenta que todo esto es investigación y trabajo futuro. No hay nada de estas mejoras de matriz que se haya comprometido en la hoja de ruta de Java para cualquier versión, a partir de este escrito (2016-02-23).


Supongamos que el código especial se agregará al compilador de java para gestionar forEach . Entonces muchas preguntas similares podrían hacerse. ¿Por qué no podemos escribir myArray.fill(0) ? ¿O myArray.copyOfRange(from, to) ? O myArray.sort() ? myArray.binarySearch() ? myArray.stream() ? Prácticamente todos los métodos estáticos en la interfaz Arrays se pueden convertir en el método correspondiente de la "clase de matriz". ¿Por qué deberían detenerse los desarrolladores de JDK en myArray.forEach() ? Sin embargo, tenga en cuenta que cada método debe agregarse no solo a la especificación classlib, sino a la especificación de lenguaje Java, que es mucho más estable y conservadora. También esto significaría que no solo la implementación de dichos métodos se convertiría en parte de la especificación, sino que también las clases como java.util.function.Consumer deberían mencionarse explícitamente en JLS (que es el argumento propuesto para forEach método). También tenga en cuenta que sería necesario agregar nuevos consumidores a la biblioteca estándar como FloatConsumer , ByteConsumer , etc. para los tipos de matriz correspondientes. Actualmente, JLS rara vez se refiere a los tipos fuera del paquete java.lang (con algunas excepciones notables como java.util.Iterator ). Esto implica cierta capa de estabilidad. El cambio propuesto es demasiado drástico para el lenguaje Java.

También tenga en cuenta que actualmente tenemos un método que podría llamarse para matrices directamente (y cuya implementación difiere del java.lang.Object ): es el método clone() . De hecho, agrega algunas partes sucias en javac e incluso en JVM, ya que debe manejarse especialmente en todas partes. Esto causa errores (por ejemplo, las referencias a los métodos se manejaron incorrectamente en Java 8 JDK-8056051). Agregar más complejidad similar en javac puede introducir errores aún más similares.

Tal característica probablemente se implementará en un futuro no muy próximo como parte de la slides . La idea es introducir alguna superclase para las matrices que se ubicarán en la biblioteca de la clase, por lo que se podrían agregar nuevos métodos simplemente escribiendo código java normal sin ajustar javac / JVM. Sin embargo, esta característica también es muy difícil ya que las matrices siempre se tratan especialmente en Java, y, hasta donde sé, aún se desconoce si se implementará y cuándo.





javac