Por que uma matriz não pode ser atribuída à Iterable?

186

com Java5, podemos escrever:

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

ou apenas usando um Iterable no loop for. Isso é muito útil.

No entanto, você não pode escrever um método genérico para iterável como este:

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

e chamando-o com uma matriz, pois não é um Iterable:

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

Estou pensando nas razões por trás dessa decisão de design.

dfa
fonte
8
Arrays.asList é bom o suficiente suponho
dfa
17
é uma questão filosófica
dfa
2
Uma boa razão para lidar com matrizes no Java 5+ é os métodos varargs.
Jeff Walker
2
@ Torsten: true, mas se você o está passando para um método que aceita um Iterable, provavelmente você não fará nenhuma alteração.
Michael Myers
5
Na verdade, Arrays.asList não é bom o suficiente porque não funciona em matrizes de tipos primitivos. A única maneira interna de iterar genericamente (em caixa) elementos de tipos primitivos é com reflexão, usando java.lang.reflect.Array, mas seu desempenho é fraco. No entanto, você pode escrever seus próprios iteradores (ou implementações de lista!) Para agrupar matrizes de tipos primitivos, se desejar.
quer

Respostas:

78

Matrizes podem implementar interfaces ( Cloneablee java.io.Serializable). Então porque não Iterable? Eu acho que Iterableforças adicionando um iteratormétodo, e matrizes não implementam métodos. char[]nem substitui toString. De qualquer forma, as matrizes de referências devem ser consideradas abaixo do ideal - use Lists. Como comentários do dfa, Arrays.asListfará a conversão explicitamente.

(Dito isso, você pode chamar clonematrizes.)

Tom Hawtin - linha de orientação
fonte
23
> "... e matrizes não implementam métodos." Eu acho que essa é outra questão filosófica; matrizes nunca foram tipos primitivos, e a filosofia Java diz que "Tudo é um objeto (exceto tipos primitivos)". Por que, então, matrizes não implementam métodos, embora existam um zilhão de operações para as quais alguém desejaria usar uma matriz desde o início. Ah, isso mesmo, as matrizes eram as únicas coleções fortemente tipadas antes que os genéricos surgissem como uma triste retrospectiva.
Fatuhoku
2
Se você tiver dados em uma matriz, é possível que você esteja executando um trabalho crítico de desempenho de baixo nível, como lidar com a leitura de bytes [] dos fluxos. A incapacidade de iterar matrizes provavelmente decorre de genéricos Java que não suportam primitivas como argumentos de tipo, como @Gareth diz abaixo.
de Drew Noakes
2
@FatuHoku A sugestão de genéricos foi uma desculpa retrospectiva incorreta. A conveniência de genéricos sempre foi apreciada. Matrizes não são primitivas (eu não disse que eram), mas são de baixo nível. A única coisa que você deseja fazer com matrizes é usá-las como um detalhe de implementação para estruturas semelhantes a vetores.
Tom Hawtin - defina
1
Iterator<T>também requer remove(T), embora seja permitido lançar um UnsupportedOperationException.
wchargin
Matrizes implementam métodos: eles implementam todos os métodos de java.lang.Object.
mhsmith
59

A matriz é um objeto, mas seus itens podem não ser. A matriz pode conter um tipo primitivo como int, que o Iterable não pode lidar. Pelo menos é o que eu acho.

Gareth Adamson
fonte
3
Isso significa que, para oferecer suporte à Iterableinterface, as matrizes primitivas devem ser especializadas para usar as classes de wrapper. Porém, nada disso é realmente importante, já que os parâmetros de tipo são todos falsos.
thejoshwolfe
8
Isso não impediria que matrizes de objetos implementassem Iterable. Também não impediria que matrizes primitivas implementassem Iterable para o tipo agrupado.
quer
1
Autoboxing poderia lidar com isso
Tim Buthe
Eu acho que esse é o motivo correto. Não funcionará satisfatoriamente para matrizes de tipos primitivos, até que os genéricos suportem tipos primitivos (por exemplo, em List<int>vez de List<Integer>etc). A corte poderia ser feito com wrappers, mas com perda de performance - e mais importante - se este hack foi feito, it'ld evitar implementá-lo corretamente em Java no futuro (por exemplo, int[].iterator()seria para sempre ser bloqueado para retornar Iterator<Integer>ao invés Iterator<int>). Talvez, os próximos tipos de valor + especialização genérica para Java (projeto valhalla) tornem as matrizes implementadas Iterable.
Bjarke
16

As matrizes devem suportar Iterable, simplesmente não, pelo mesmo motivo que as matrizes .NET não suportam uma interface que permite acesso aleatório somente leitura por posição (não existe uma interface definida como padrão). Basicamente, as estruturas costumam ter pequenas lacunas irritantes, o que não vale a pena reparar. Não importaria se poderíamos corrigi-los da melhor maneira possível, mas geralmente não podemos.

ATUALIZAÇÃO: Para ser imparcial, mencionei matrizes .NET que não suportam uma interface que suporta acesso aleatório por posição (consulte também meu comentário). Mas no .NET 4.5 essa interface exata foi definida e é suportada por matrizes e pela List<T>classe:

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

Tudo ainda não é perfeito porque a interface da lista mutável IList<T>não herda IReadOnlyList<T>:

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

Talvez exista uma possível compatibilidade com essa alteração.

Se houver algum progresso em coisas semelhantes nas versões mais recentes do Java, eu estaria interessado em saber nos comentários! :)

Daniel Earwicker
fonte
8
Matrizes .NET implementar a interface IList
Tom Gillen
2
@ Afid - eu disse acesso aleatório somente leitura . IList<T>expõe operações para modificação. Seria ótimo se IList<T>tinha herdado algo como uma IReadonlyList<T>interface, que teria apenas tinha Counte T this[int]e herdou IEnumerable<T>(que já suporta enumeração somente leitura). Outro grande coisa seria uma interface para a obtenção de um recenseador-ordem inversa, que o Reversemétodo de extensão pode consultar (assim como as Countconsultas de método de extensão para ICollectionotimizar si mesmo.)
Daniel Earwicker
Sim, seria muito melhor se as coisas tivessem sido projetadas dessa maneira. A interface IList define as propriedades IsReadOnly e IsFixedSize, que são implementadas adequadamente por matriz. Isso sempre me pareceu uma maneira muito ruim de fazê-lo, pois não oferece tempo de compilação para verificar se a lista fornecida é de fato somente leitura, e raramente vejo código que verifica essas propriedades.
Tom Gillen
1
Matrizes no .NET implementam IListe ICollectiondesde o .NET 1.1 IList<T>e ICollection<T>desde o .NET 2.0. Esse é outro caso em que o Java está muito atrás da concorrência.
Amir Abiri
@ TomGillen: Meu maior problema IListé que ele não fornece mais atributos consultáveis. Eu diria que um conjunto adequado deve incluir IsUpdateable, IsResizable, IsReadOnly, IsFixedSize e ExistingElementsAreImmutable para iniciantes. A questão de saber se o código que uma referência pode, sem conversão de tipo, modificar uma lista é separada das questões sobre se o código que contém uma referência a uma lista que não deveria modificar pode compartilhar com segurança essa referência diretamente com o código externo ou se pode assumir com segurança que algum aspecto da lista nunca será alterado.
Supercat
14

Infelizmente, matrizes não são ' classsuficientes'. Eles não implementam a Iterableinterface.

Enquanto matrizes agora são objetos que implementam Clonable e Serializable, acredito que uma matriz não é um objeto no sentido normal e não implementa a interface.

A razão pela qual você pode usá-los em cada loop é porque a Sun adicionou um pouco de açúcar sintático para matrizes (é um caso especial).

Como as matrizes começaram como 'quase objetos' no Java 1, seria uma mudança drástica demais para torná-las objetos reais em Java.

jjnguy
fonte
14
Ainda assim, há açúcar para o loop for-each, então por que não pode haver açúcar para o Iterable?
Michael Myers
8
@ mmyers: o açúcar usado para cada um é açúcar em tempo de compilação . Isso é muito mais fácil do que o açúcar VM . Tendo dito que, matrizes .NET são significativamente melhores nesta área ...
Jon Skeet
12
Matrizes podem implementar interfaces. Eles implementam Cloneablee Serializableinterfaces.
notnoop 21/07/2009
34
matrizes java são objetos em todos os sentidos. Por favor, remova esse pouco de informação incorreta. Eles simplesmente não implementam o Iterable.
ykaganovich 21/07/2009
5
Uma matriz é um objeto. Ele é útil : métodos P como wait (), wait (n), wait (n, m), notify (), notifyAll (), finalize (), uma implementação inútil de toString () O único método útil é getClass () .
Peter Peterrey
1

O compilador na verdade converte o for eachem uma matriz em um forloop simples com uma variável de contador.

Compilando o seguinte

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

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

e, em seguida, descompilar o arquivo .class gera

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
fonte