Mengapa array tidak dapat ditugaskan ke Iterable?

186

dengan Java5 kita dapat menulis:

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

atau hanya menggunakan Iterable di for loop. Ini sangat berguna.

Namun Anda tidak dapat menulis metode generik untuk iterable seperti ini:

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

dan menyebutnya dengan array karena itu bukan Iterable:

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

Saya bertanya-tanya tentang alasan di balik keputusan desain ini.

dfa
sumber
8
Arrays.asList cukup baik kurasa
1780 dfa
17
itu adalah pertanyaan filosofis
dfa
2
alasan bagus untuk berurusan dengan array di Java 5+ adalah metode varargs.
Jeff Walker
2
@ Torsten: true, tetapi jika Anda meneruskannya ke metode yang menerima Iterable, Anda kemungkinan besar tidak akan melakukan perubahan apa pun.
Michael Myers
5
Sebenarnya, Arrays.asList tidak cukup baik karena tidak bekerja pada array tipe primitif. Satu-satunya cara built-in untuk secara umum beralih (kotak) elemen tipe primitif adalah dengan refleksi, menggunakan java.lang.reflect.Array, tetapi kinerjanya lemah. Namun, Anda dapat menulis iterator Anda sendiri (atau implementasi Daftar!) Untuk membungkus array tipe primitif jika Anda mau.
Boann

Jawaban:

78

Array dapat mengimplementasikan antarmuka ( Cloneabledan java.io.Serializable). Jadi mengapa tidak Iterable? Saya kira Iterablememaksa menambahkan iteratormetode, dan array tidak menerapkan metode. char[]bahkan tidak menimpanya toString. Pokoknya, array referensi harus dianggap kurang dari ideal - gunakan Lists. Seperti komentar dfa, Arrays.asListakan melakukan konversi untuk Anda, secara eksplisit.

(Karena itu, Anda dapat memanggil clonearray.)

Tom Hawtin - tackline
sumber
23
> "... dan array tidak mengimplementasikan metode." Saya pikir ini adalah pertanyaan filosofis lain; array tidak pernah tipe primitif, dan filosofi Java membaca bahwa "Semuanya adalah objek (kecuali tipe primitif)". Jadi, mengapa, array tidak mengimplementasikan metode walaupun ada banyak trilyun operasi yang ingin digunakan oleh sebuah array dari awal. Oh, itu benar, array adalah satu-satunya koleksi yang sangat diketik sebelum obat generik muncul sebagai tinjauan balik yang menyedihkan.
fatuhoku
2
Jika Anda memiliki data dalam sebuah array, mungkin Anda melakukan pekerjaan tingkat rendah, kinerja kritis seperti berurusan dengan pembacaan byte [] dari stream. Ketidakmampuan untuk mengulangi array mungkin berasal dari generik Java yang tidak mendukung primitif sebagai argumen tipe, seperti yang dikatakan @Gareth di bawah ini.
Drew Noakes
2
@FatuHoku Menyarankan obat-obatan generik ke belakang merupakan kesalahan. Keinginan generik selalu dihargai. Array bukan primitif (saya tidak mengatakan mereka), tetapi mereka level rendah. Satu hal yang ingin Anda lakukan dengan array adalah menggunakannya sebagai detail implementasi untuk struktur seperti vektor.
Tom Hawtin - tackline
1
Iterator<T>juga membutuhkan remove(T), meskipun diizinkan untuk melempar UnsupportedOperationException.
wchargin
Array menerapkan metode: mereka menerapkan semua metode java.lang.Object.
mhsmith
59

Array adalah Object, tetapi itemnya mungkin bukan. Array mungkin memiliki tipe primitif seperti int, yang tidak bisa diatasi oleh Iterable. Setidaknya itulah yang kurasa.

Gareth Adamson
sumber
3
Ini berarti untuk mendukung Iterableantarmuka, array primitif harus dikhususkan untuk menggunakan kelas wrapper. Tidak ada yang benar-benar masalah besar, karena semua parameter parameter tetap palsu.
thejoshwolfe
8
Ini tidak akan mencegah array Obyek mengimplementasikan Iterable. Itu juga tidak akan mencegah array primitif dari menerapkan Iterable untuk tipe yang dibungkus.
Boann
1
Autoboxing dapat menangani ini
Tim Büthe
Saya pikir ini alasan yang tepat. Ini tidak akan berfungsi memuaskan untuk array tipe primitif, sampai Generics mendukung tipe primitif (misalnya, List<int>bukan List<Integer>, dll). Peretasan dapat dilakukan dengan pembungkus tetapi dengan kehilangan kinerja - dan yang lebih penting - jika peretasan ini dilakukan, peretasan tersebut akan mencegah penerapannya dengan benar di Jawa di masa mendatang (misalnya int[].iterator()akan selamanya dikunci untuk kembali Iterator<Integer>daripada Iterator<int>). Mungkin, tipe-nilai + generik-spesialisasi yang akan datang untuk Java (proyek valhalla) akan membuat array diimplementasikan Iterable.
Bjarke
16

Array harus didukung Iterable, mereka tidak, karena alasan yang sama. NET array tidak mendukung antarmuka yang memungkinkan akses acak hanya baca oleh posisi (tidak ada antarmuka seperti yang didefinisikan sebagai standar). Pada dasarnya, kerangka kerja sering memiliki celah kecil yang menjengkelkan di dalamnya, yang tidak layak waktu untuk diperbaiki. Tidak masalah jika kita dapat memperbaikinya sendiri secara optimal, tetapi seringkali kita tidak bisa memperbaikinya.

UPDATE: Agar adil, saya sebutkan .NET array tidak mendukung antarmuka yang mendukung akses acak berdasarkan posisi (lihat juga komentar saya). Namun dalam .NET 4.5 antarmuka yang tepat telah ditentukan dan didukung oleh array dan List<T>kelas:

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

Semua masih belum cukup sempurna karena antarmuka daftar yang dapat diubah IList<T>tidak mewarisi IReadOnlyList<T>:

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

Mungkin ada kemungkinan kompatibilitas gotcha dengan perubahan seperti itu.

Jika ada kemajuan pada hal serupa di versi Java yang lebih baru, saya akan tertarik untuk mengetahui di komentar! :)

Daniel Earwicker
sumber
8
Array .NET menerapkan antarmuka IList
Tom Gillen
2
@Aphid - Aku kata dibaca akses acak. IList<T>memperlihatkan operasi untuk memodifikasi. Akan lebih bagus jika IList<T>mewarisi sesuatu seperti IReadonlyList<T>antarmuka, yang baru saja Countdan T this[int]dan diwarisi IEnumerable<T>(yang sudah mendukung enumerasi readonly). Hal hebat lainnya adalah antarmuka untuk mendapatkan enumerator urutan terbalik, yang dapat diminta oleh Reversemetode ekstensi (seperti Countmetode ekstensi yang meminta ICollectionuntuk mengoptimalkan dirinya sendiri.)
Daniel Earwicker
Ya, akan jauh lebih baik jika semuanya dirancang seperti itu. Antarmuka IList mendefinisikan properti IsReadOnly dan IsFixedSize, yang diimplementasikan dengan tepat oleh array. Itu selalu mengejutkan saya sebagai cara yang sangat buruk untuk melakukannya, karena ia tidak menawarkan waktu kompilasi untuk memeriksa bahwa daftar yang Anda berikan ternyata hanya bisa dibaca, dan saya sangat jarang melihat kode yang memeriksa properti ini.
Tom Gillen
1
Array dalam .NET implement IList& ICollectionsejak. NET 1.1, dan IList<T>dan ICollection<T>.NET 2.0. Ini adalah kasus lain di mana Jawa jauh di belakang kompetisi.
Amir Abiri
@ TomGillen: Masalah terbesar saya IListadalah tidak menyediakan atribut yang lebih dapat ditanyakan. Saya akan mengatakan set yang tepat harus mencakup IsUpdateable, IsResizable, IsReadOnly, IsFixedSize, dan ExistingElementsAreImmutable sebagai permulaan. Pertanyaan apakah kode referensi dapat, tanpa typecasting, memodifikasi daftar terpisah dari pertanyaan apakah kode yang menyimpan referensi ke daftar yang tidak seharusnya diubah dapat dengan aman membagikan referensi itu langsung dengan kode luar, atau apakah dapat dengan aman mengasumsikan beberapa aspek dari daftar tidak akan pernah berubah.
supercat
14

Sayangnya, array tidak ' class-cukup'. Mereka tidak mengimplementasikan Iterableantarmuka.

Sementara array sekarang objek yang mengimplementasikan Clonable dan Serializable, saya percaya array bukan objek dalam arti normal , dan tidak mengimplementasikan antarmuka.

Alasan Anda dapat menggunakannya dalam untuk-setiap loop adalah karena Sun menambahkan gula sintaks untuk array (ini adalah kasus khusus).

Karena array dimulai sebagai 'hampir objek' dengan Java 1, itu akan menjadi perubahan yang terlalu drastis untuk menjadikannya objek nyata di Jawa.

jjnguy
sumber
14
Namun, ada gula untuk setiap loop, jadi mengapa tidak ada gula untuk Iterable?
Michael Myers
8
@ pengacara: Gula yang digunakan untuk masing-masing gula adalah waktu kompilasi . Itu jauh lebih mudah dilakukan daripada gula VM . Setelah mengatakan yang, NET array secara signifikan lebih baik di daerah ini ...
Jon Skeet
12
Array dapat mengimplementasikan antarmuka. Mereka mengimplementasikan Cloneabledan Serializableantarmuka.
notnoop
34
array java adalah objek dalam semua pengertian. Harap hapus sedikit informasi yang salah itu. Mereka hanya tidak menerapkan Iterable.
ykaganovich
5
Array adalah Object. Ini mendukung berguna : metode P seperti wait (), wait (n), wait (n, m), notify (), notifyAll (), finalize (), implementasi toString yang tidak berguna () Satu-satunya metode yang berguna adalah getClass () .
Peter Lawrey
1

Compiler sebenarnya menerjemahkan for eachpada array menjadi forloop sederhana dengan variabel counter.

Kompilasi yang berikut ini

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

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

dan kemudian mendekompilasi hasil file .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);
    }
}
das Keks
sumber