Почему массив нельзя присвоить Iterable?

186

с Java5 мы можем написать:

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

или просто используя Iterable в цикле for. Это очень удобно.

Однако вы не можете написать универсальный метод для повторяемого, как это:

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

и вызывая его с массивом, поскольку он не является Iterable:

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

Я задаюсь вопросом о причинах этого дизайнерского решения.

DFA
источник
8
Arrays.asList достаточно хорош, я полагаю
dfa
17
это философский вопрос
dfa
2
Хорошая причина для работы с массивами в Java 5+ - это методы varargs.
Джефф Уокер
2
@Torsten: true, но если вы передаете его методу, который принимает Iterable, скорее всего, вы не внесете никаких изменений.
Майкл Майерс
5
На самом деле Arrays.asList недостаточно хорош, потому что он не работает с массивами примитивных типов. Единственный встроенный способ для общей итерации (в штучной упаковке) элементов примитивных типов - это использование отражения java.lang.reflect.Array, но его производительность слабая. Однако вы можете написать свои собственные итераторы (или реализации List!), Чтобы обернуть массивы примитивных типов, если хотите.
Boann

Ответы:

78

Массивы могут реализовывать интерфейсы ( Cloneableи java.io.Serializable). Так почему бы и нет Iterable? Я предполагаю, что Iterableсилы добавляют iteratorметод, а массивы не реализуют методы. char[]даже не переопределить toString. В любом случае, массивы ссылок следует считать не идеальными - используйте Lists. Как комментирует dfa, Arrays.asListмы сделаем конвертацию за вас.

(Сказав это, вы можете вызвать cloneмассивы.)

Том Хотин - Tackline
источник
23
> "... и массивы не реализуют методы." Я думаю, что это еще один философский вопрос; массивы никогда не были примитивными типами, и философия Java гласит: «Все является объектом (кроме примитивных типов)». Почему же тогда массивы не реализуют методы, даже несмотря на то, что существует множество операций, для которых желательно использовать массив с самого начала. О, верно, массивы были единственными строго типизированными коллекциями до того, как дженерики появились как печальное задним числом.
фатухоку
2
Если у вас есть данные в массиве, возможно, вы выполняете низкоуровневую работу, критичную для производительности, например, работу с чтением byte [] из потоков. Невозможность повторять массивы, вероятно, проистекает из обобщений Java, не поддерживающих примитивы в качестве аргументов типа, как @Gareth говорит ниже.
Дрю Ноакс
2
@FatuHoku Предлагать дженерики, к сожалению, задним числом неверно. Желательность дженериков всегда ценилась. Массивы не являются примитивами (я не говорил, что они были), но они низкоуровневые. Единственное, что вы хотите сделать с массивами, это использовать их как детали реализации для векторных структур.
Том Хотин -
1
Iterator<T>также требует remove(T), хотя это разрешено бросать UnsupportedOperationException.
wchargin
Массивы реализуют методы: они реализуют все методы java.lang.Object.
mhsmith
59

Массив является объектом, но его элементы могут не быть. Массив может содержать тип примитива типа int, с которым Iterable не может справиться. По крайней мере, это то, что я считаю.

Гарет Адамсон
источник
3
Это означает, что для поддержки Iterableинтерфейса примитивные массивы должны быть специализированы для использования классов-оболочек. Ничего из этого не имеет большого значения, поскольку параметры типа все равно являются фальшивыми.
thejoshwolfe
8
Это не помешает объектным массивам реализовать Iterable. Это также не помешало бы примитивным массивам реализовать Iterable для обернутого типа.
Boann
1
Автобокс может справиться с этим
Тим Büthe
Я думаю, что это правильная причина. Он не будет работать удовлетворительно для массивов примитивных типов, пока Generics не будет поддерживать примитивные типы (например, List<int>вместо List<Integer>и т. Д.). Взлом может быть осуществлен с помощью упаковщиков, но с потерей производительности - и что еще более важно - если этот взлом будет осуществлен, это предотвратит его надлежащую реализацию в Java в будущем (например int[].iterator(), навсегда будет заблокировано для возврата, Iterator<Integer>а не Iterator<int>). Возможно, грядущие значения-типы + обобщенная специализация для Java (проект valhalla) заставят массивы реализовать Iterable.
Бьярке
16

Массивы должны поддерживать Iterable, они просто не поддерживают , по той же причине, по которой массивы .NET не поддерживают интерфейс, который обеспечивает произвольный доступ только для чтения по позиции (такой интерфейс не определен как стандартный). По сути, фреймворки часто имеют в себе небольшие пробелы, которые никому не стоит исправлять. Не имело бы значения, если бы мы могли исправить их самостоятельно каким-то оптимальным способом, но часто мы не можем.

ОБНОВЛЕНИЕ: Чтобы быть беспристрастным, я упомянул массивы .NET, не поддерживающие интерфейс, который поддерживает произвольный доступ по позиции (см. Также мой комментарий). Но в .NET 4.5 этот точный интерфейс был определен и поддерживается массивами и List<T>классом:

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

Все еще не совсем идеально, потому что интерфейс изменяемого списка IList<T>не наследует IReadOnlyList<T>:

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

Может быть, есть вероятность обратной совместимости с таким изменением.

Если есть какие-то успехи в подобных вещах в новых версиях Java, мне было бы интересно узнать в комментариях! :)

Дэниел Уорвикер
источник
8
.NET массивы реализуют интерфейс IList
Том Гиллен
2
@Aphid - я сказал только для чтения произвольного доступа. IList<T>выставляет операции для модификации. Было бы замечательно, если IList<T>бы унаследовал что-то вроде IReadonlyList<T>интерфейса, который бы только что имел Countи T this[int]и унаследовал IEnumerable<T>(который уже поддерживает перечисление только для чтения). Еще одна интересная вещь - это интерфейс для получения перечислителя обратного порядка, к которому может обращаться Reverseметод расширения (так же, как Countметод расширения запрашивает для ICollectionоптимизации).
Даниэль Эрвикер,
Да, было бы намного лучше, если бы все было разработано таким образом. Интерфейс IList определяет свойства IsReadOnly и IsFixedSize, которые соответствующим образом реализуются массивом. Это всегда казалось мне очень плохим способом сделать это, так как он не предлагает проверки времени компиляции, что список, который вы даете, фактически доступен только для чтения, и я очень редко вижу код, который проверяет эти свойства.
Том Гиллен
1
Массивы в .NET реализуют IListи ICollectionс .NET 1.1, IList<T>и ICollection<T>с .NET 2.0. Это еще один случай, когда Java сильно отстает от конкурентов.
Амир Абири
@ TomGillen: Моя самая большая проблема в IListтом, что он не предоставляет больше запрашиваемых атрибутов. Я бы сказал, что правильный набор должен включать IsUpdateable, IsResizable, IsReadOnly, IsFixedSize и ExistingElementsAreImmutable для начинающих. Вопрос о том, может ли ссылка, на которую ссылается код, изменять тип без изменения типа, отдельно от вопросов о том, может ли код, содержащий ссылку на список, который он не должен изменять, безопасно делиться этой ссылкой напрямую с внешним кодом или можно смело предположить, что какой-то аспект списка никогда не изменится.
суперкат
14

К сожалению, массивов не classдостаточно. Они не реализуют Iterableинтерфейс.

Хотя массивы теперь являются объектами, которые реализуют Clonable и Serializable, я считаю, что массив не является объектом в обычном смысле и не реализует интерфейс.

Причина, по которой вы можете использовать их в циклах for-each, заключается в том, что Sun добавила синтетический сахар для массивов (это особый случай).

Так как массивы начинались как «почти объекты» в Java 1, было бы слишком радикально изменить их, чтобы сделать их реальными объектами в Java.

jjnguy
источник
14
Тем не менее, есть сахар для цикла for-each, так почему не может быть сахара для Iterable?
Майкл Майерс
8
@mmyers: сахар, используемый для каждого - это сахар во время компиляции . Это гораздо проще сделать, чем сахар VM . Сказав, что, .NET массивы значительно лучше в этой области ...
Джон Скит
12
Массивы могут реализовывать интерфейсы. Они реализуют Cloneableи Serializableинтерфейсы.
Notnoop
34
Java-массивы являются объектами во всех смыслах. Пожалуйста, удалите эту дезинформацию. Они просто не реализуют Iterable.
Икаганович
5
Массив - это Объект. Поддерживаются полезные : P методы, такие как wait (), wait (n), wait (n, m), notify (), notifyAll (), finalize (), бессмысленная реализация toString (). Единственный полезный метод - getClass () ,
Питер Лори
1

Компилятор фактически переводит for eachмассив в простой forцикл с переменной счетчика.

Составление следующего

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

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

а затем декомпиляция файла .class дает

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);
    }
}
дас кекс
источник