¿Por qué una matriz no es asignable a Iterable?

186

con Java5 podemos escribir:

Foo[] foos = ...
for (Foo foo : foos) 

o simplemente usando un Iterable en el ciclo for. Esto es muy útil.

Sin embargo, no puede escribir un método genérico para iterable como este:

public void bar(Iterable<Foo> foos) { .. }

y llamarlo con una matriz ya que no es un Iterable:

Foo[] foos = { .. };
bar(foos);  // compile time error 

Me pregunto las razones detrás de esta decisión de diseño.

dfa
fuente
8
Arrays.asList es lo suficientemente bueno supongo
dfa
17
es una pregunta filosófica
dfa
2
Una buena razón para tratar con matrices en Java 5+ son los métodos varargs.
Jeff Walker
2
@Torsten: cierto, pero si lo pasa a un método que acepta un Iterable, es probable que no realice ningún cambio de todos modos.
Michael Myers
55
En realidad, Arrays.asList no es lo suficientemente bueno porque no funciona en matrices de tipos primitivos. La única forma integrada de iterar genéricamente (encuadrado) elementos de tipos primitivos es con reflexión, usando java.lang.reflect.Array, pero su rendimiento es débil. Sin embargo, puede escribir sus propios iteradores (¡o listar implementaciones!) Para envolver matrices de tipos primitivos si lo desea.
Boann

Respuestas:

78

Las matrices pueden implementar interfaces ( Cloneabley java.io.Serializable). Entonces, ¿por qué no Iterable? Supongo que Iterableobliga a agregar un iteratormétodo, y las matrices no implementan métodos. char[]Ni siquiera anula toString. De todos modos, los conjuntos de referencias deben considerarse menos que ideales: use Lists. Como comenta dfa,Arrays.asList hará la conversión por usted, explícitamente.

(Dicho esto, puede llamar clonea las matrices).

Tom Hawtin - tackline
fuente
23
> "... y las matrices no implementan métodos". Creo que esta es otra pregunta filosófica; Las matrices nunca fueron tipos primitivos, y la filosofía de Java dice que "Todo es un objeto (excepto los tipos primitivos)". ¿Por qué, entonces, las matrices no implementan métodos a pesar de que hay miles de millones de operaciones para las que uno desearía utilizar una matriz desde el principio? Ah, es cierto, las matrices eran las únicas colecciones fuertemente tipadas antes de que los genéricos aparecieran como una triste retrospectiva.
fatuhoku
2
Si tiene datos en una matriz, es posible que esté haciendo un trabajo crítico de bajo nivel de rendimiento, como lidiar con la lectura del byte [] de las secuencias. La incapacidad para iterar matrices probablemente se deba a que los genéricos de Java no admiten primitivas como argumentos de tipo, como @Gareth dice a continuación.
Drew Noakes
2
@FatuHoku Sugerir que los genéricos eran una retrospectiva lamentable es incorrecta. La conveniencia de los genéricos siempre fue apreciada. Las matrices no son primitivas (no dije que lo fueran), pero son de bajo nivel. Lo único que desea hacer con las matrices es usarlas como un detalle de implementación para estructuras tipo vector.
Tom Hawtin - tackline
1
Iterator<T>también requiere remove(T), aunque está permitido lanzar un UnsupportedOperationException.
wchargin
Las matrices implementan métodos: implementan todos los métodos de java.lang.Object.
mhsmith
59

La matriz es un objeto, pero sus elementos pueden no serlo. La matriz puede contener un tipo primitivo como int, que Iterable no puede hacer frente. Al menos eso creo.

Gareth Adamson
fuente
3
Esto significa que para admitir la Iterableinterfaz, las matrices primitivas deben estar especializadas para usar las clases envolventes. Sin embargo, nada de esto es realmente un gran problema, ya que los parámetros de tipo son todos falsos de todos modos.
thejoshwolfe
8
Esto no evitaría que las matrices de objetos implementen Iterable. Tampoco evitaría que las matrices primitivas implementen Iterable para el tipo envuelto.
Boann
1
Autoboxing podría manejar esto
Tim Büthe
Creo que esta es la razón correcta. No funcionará satisfactoriamente para matrices de tipos primitivos, hasta que los genéricos admitan tipos primitivos (por ejemplo, en List<int>lugar de List<Integer>, etc.). Se podría realizar un hack con envoltorios pero con una pérdida de rendimiento, y lo que es más importante, si se realizara este truco, evitaría implementarlo correctamente en Java en el futuro (por ejemplo int[].iterator(), se bloquearía para siempre en Iterator<Integer>lugar de hacerlo Iterator<int>). Quizás, los próximos tipos de valor + especialización genérica para Java (proyecto valhalla) harán que las matrices se implementen Iterable.
Bjarke
16

Las matrices deberían admitir Iterable, simplemente no lo hacen, por la misma razón que las matrices .NET no admiten una interfaz que permita el acceso aleatorio de solo lectura por posición (no existe una interfaz definida como estándar). Básicamente, los marcos a menudo tienen pequeñas brechas molestas, que no vale la pena el tiempo de arreglar. No importaría si pudiéramos arreglarlos nosotros mismos de una manera óptima, pero a menudo no podemos.

ACTUALIZACIÓN: Para ser imparcial, mencioné que las matrices .NET no admiten una interfaz que admita el acceso aleatorio por posición (ver también mi comentario). Pero en .NET 4.5 esa interfaz exacta se ha definido y es compatible con las matrices y elList<T> clase:

IReadOnlyList<int> a = new[] {1, 2, 3, 4};
IReadOnlyList<int> b = new List<int> { 1, 2, 3, 4 };

Todo aún no es perfecto porque la interfaz de lista mutable IList<T> no hereda IReadOnlyList<T>:

IList<int> c = new List<int> { 1, 2, 3, 4 };
IReadOnlyList<int> d = c; // error

Tal vez hay un posible problema de compatibilidad con versiones anteriores con tal cambio.

Si hay algún progreso en cosas similares en las versiones más recientes de Java, ¡me interesaría saber en los comentarios! :)

Daniel Earwicker
fuente
8
Las matrices .NET implementan la interfaz IList
Tom Gillen
2
@Aphid - Dije acceso aleatorio de solo lectura . IList<T>expone operaciones para modificar. Sería muy bueno si IList<T>había heredado algo así como una IReadonlyList<T>interfaz, lo que habría tenido solo County T this[int]e heredada IEnumerable<T>(que ya soporta la enumeración de sólo lectura). Otra gran cosa sería una interfaz para obtener un enumerador de orden inverso, que el Reversemétodo de extensión podría consultar (al igual que el Countmétodo de extensión busca ICollectionoptimizarse)
Daniel Earwicker
Sí, sería mucho mejor si las cosas hubieran sido diseñadas de esa manera. La interfaz IList define las propiedades IsReadOnly e IsFixedSize, que se implementan adecuadamente por array. Sin embargo, eso siempre me ha parecido una muy mala forma de hacerlo, ya que no ofrece tiempo de compilación para verificar que la lista que se le da es de solo lectura, y rara vez veo un código que verifique estas propiedades.
Tom Gillen
1
Matrices en .NET implement IList& ICollectiondesde .NET 1.1, IList<T>y ICollection<T>desde .NET 2.0. Este es otro caso en el que Java está muy por detrás de la competencia.
Amir Abiri el
@TomGillen: Mi mayor problema IListes que no proporciona más atributos consultables. Yo diría que un conjunto adecuado debería incluir IsUpdateable, IsResizable, IsReadOnly, IsFixedSize y ExistingElementsAreImmutable para empezar. La cuestión de si el código de una referencia puede, sin encasillamiento, modificar una lista es independiente de las preguntas de si el código que contiene una referencia a una lista que no debe modificar puede compartir esa referencia directamente con el código externo, o si puede asumir con seguridad que algún aspecto de la lista nunca cambiará.
supercat
14

Desafortunadamente, las matrices no son " classsuficientes". No implementan elIterable interfaz.

Si bien las matrices ahora son objetos que implementan Clonable y Serializable, creo que una matriz no es un objeto en el sentido normal , y no implementa la interfaz.

La razón por la que puede usarlos en los bucles for-each es porque Sun agregó algo de azúcar sintético para los arrays (es un caso especial).

Dado que las matrices comenzaron como 'casi objetos' con Java 1, sería un cambio demasiado drástico hacerlos objetos reales en Java.

jjnguy
fuente
14
Aún así, hay azúcar para cada ciclo, entonces ¿por qué no puede haber azúcar para Iterable?
Michael Myers
8
@mmyers: El azúcar que se usa en for-each es azúcar en tiempo de compilación . Eso es mucho más fácil de hacer que el azúcar VM . Habiendo dicho que, a matrices .NET son significativamente mejores en esta zona ...
Jon Skeet
12
Las matrices pueden implementar interfaces. Implementan Cloneablee Serializableinterfaces.
notnoop
34
Las matrices Java son objetos en todos los sentidos. Elimina esa información errónea. Simplemente no implementan Iterable.
ykaganovich el
55
Una matriz es un objeto. Es útil : métodos P como wait (), wait (n), wait (n, m), notify (), notifyAll (), finalize (), una implementación sin sentido de toString () El único método útil es getClass () .
Peter Lawrey
1

El compilador en realidad traduce el for eachen una matriz en un forbucle simple con una variable de contador.

Compilando lo siguiente

public void doArrayForEach() {
    int[] ints = new int[5];

    for(int i : ints) {
        System.out.println(i);
    }
}

y luego descompilando el archivo .class produce

public void doArrayForEach() {
    int[] ints = new int[5];
    int[] var2 = ints;
    int var3 = ints.length;

    for(int var4 = 0; var4 < var3; ++var4) {
        int i = var2[var4];
        System.out.println(i);
    }
}
das Keks
fuente