Apakah ada alasan mengapa desainer Java merasa bahwa variabel lokal tidak boleh diberi nilai default? Serius, jika variabel instan dapat diberi nilai default, lalu mengapa kita tidak bisa melakukan hal yang sama untuk variabel lokal?
Dan itu juga menyebabkan masalah seperti yang dijelaskan dalam komentar ini ke posting blog :
Aturan ini paling membuat frustrasi ketika mencoba menutup sumber daya di blok terakhir. Jika saya memberi contoh sumber daya dalam percobaan, tetapi mencoba menutupnya pada akhirnya, saya mendapatkan kesalahan ini. Jika saya memindahkan contoh di luar percobaan, saya mendapatkan kesalahan lain yang menyatakan bahwa itu harus dalam percobaan.
Sangat membuat frustasi.
java
variables
initialization
Shivasubramanian A
sumber
sumber
Jawaban:
Variabel lokal dideklarasikan sebagian besar untuk melakukan beberapa perhitungan. Jadi itu keputusan programmer untuk menetapkan nilai variabel dan tidak boleh mengambil nilai default. Jika programmer, secara tidak sengaja, tidak menginisialisasi variabel lokal dan mengambil nilai default, maka outputnya bisa berupa nilai yang tidak terduga. Jadi dalam kasus variabel lokal, kompilator akan meminta programmer untuk menginisialisasi dengan beberapa nilai sebelum mereka mengakses variabel untuk menghindari penggunaan nilai yang tidak ditentukan.
sumber
"Masalah" yang Anda tautkan tampaknya menggambarkan situasi ini:
Keluhan pemberi komentar adalah bahwa kompilator menolak baris di
finally
bagian tersebut, mengklaim bahwaso
mungkin tidak diinisialisasi. Komentar tersebut kemudian menyebutkan cara lain untuk menulis kode, mungkin seperti ini:Pemberi komentar tidak senang dengan solusi itu karena kompilator kemudian mengatakan bahwa kode "harus dalam percobaan." Saya kira itu berarti beberapa kode mungkin menimbulkan pengecualian yang tidak ditangani lagi. Saya tidak yakin. Tidak ada versi kode saya yang menangani pengecualian apa pun, jadi apa pun yang terkait dengan pengecualian di versi pertama harus berfungsi sama di versi kedua.
Bagaimanapun, versi kode kedua ini adalah cara yang benar untuk menulisnya. Di versi pertama, pesan kesalahan penyusun benar. The
so
variabel mungkin uninitialized. Secara khusus, jikaSomeObject
konstruktor gagal,so
tidak akan diinisialisasi, sehingga akan menjadi kesalahan untuk mencoba memanggilso.CleanUp
. Selalu masuk ketry
bagian setelah Anda mendapatkan sumber daya yangfinally
diselesaikan bagian tersebut.The
try
-finally
blok setelahso
inisialisasi ada hanya untuk melindungiSomeObject
contoh, untuk memastikan itu akan dibersihkan tidak peduli apa pun yang terjadi. Jika ada lain hal yang perlu untuk menjalankan, tetapi mereka tidak terkait dengan apakahSomeObject
contoh itu properti dialokasikan, maka mereka harus pergi di laintry
-finally
blok, mungkin salah satu yang membungkus yang saya telah menunjukkan.Mewajibkan variabel untuk ditetapkan secara manual sebelum digunakan tidak menyebabkan masalah nyata. Ini hanya menyebabkan kerepotan kecil, tetapi kode Anda akan lebih baik untuk itu. Anda akan memiliki variabel dengan cakupan yang lebih terbatas, dan
try
-finally
blok yang tidak mencoba melindungi terlalu banyak.Jika variabel lokal memiliki nilai default, maka
so
pada contoh pertama adalahnull
. Itu tidak akan benar-benar menyelesaikan apa pun. Alih-alih mendapatkan kesalahan waktu kompilasi difinally
blok, Anda akanNullPointerException
bersembunyi di sana yang mungkin menyembunyikan pengecualian lain apa pun yang dapat terjadi di bagian "Lakukan beberapa pekerjaan di sini" pada kode. (Atau apakah pengecualian difinally
bagian secara otomatis dikaitkan dengan pengecualian sebelumnya? Saya tidak ingat. Meski begitu, Anda akan memiliki pengecualian tambahan di jalan yang asli.)sumber
Selain itu, dalam contoh di bawah ini, pengecualian mungkin telah dilemparkan ke dalam konstruksi SomeObject, dalam hal ini variabel 'so' akan menjadi null dan panggilan ke CleanUp akan memunculkan NullPointerException
Yang cenderung saya lakukan adalah ini:
sumber
Perhatikan bahwa variabel instance / anggota terakhir tidak diinisialisasi secara default. Karena itu sudah final dan tidak bisa diubah dalam program setelahnya. Itulah alasan mengapa Java tidak memberikan nilai default apa pun untuk mereka dan memaksa pemrogram untuk menginisialisasi.
Di sisi lain, variabel anggota non-final dapat diubah nanti. Oleh karena itu, kompilator tidak membiarkannya tetap tidak dijalankan, tepatnya, karena itu dapat diubah nanti. Mengenai variabel lokal, cakupan variabel lokal jauh lebih sempit. Compiler tahu kapan akan digunakan. Oleh karena itu, memaksa programmer untuk menginisialisasi variabel masuk akal.
sumber
Jawaban sebenarnya untuk pertanyaan Anda adalah karena variabel metode dibuat dengan hanya menambahkan angka ke penunjuk tumpukan. Untuk membidiknya akan menjadi langkah ekstra. Untuk variabel kelas, variabel tersebut dimasukkan ke dalam memori yang diinisialisasi di heap.
Mengapa tidak mengambil langkah ekstra? Ambil langkah mundur - Tidak ada yang menyebutkan bahwa "peringatan" dalam kasus ini adalah Hal yang Sangat Baik.
Anda tidak boleh menginisialisasi variabel Anda ke nol atau nol pada lintasan pertama (saat Anda pertama kali mengkodekannya). Baik tetapkan ke nilai sebenarnya atau jangan tetapkan sama sekali karena jika tidak, maka java dapat memberi tahu Anda saat Anda benar-benar mengacau. Ambil jawaban Electric Monk sebagai contoh yang bagus. Dalam kasus pertama, sebenarnya sangat berguna bahwa ini memberi tahu Anda bahwa jika try () gagal karena konstruktor SomeObject melemparkan pengecualian, maka Anda akan berakhir dengan NPE pada akhirnya. Jika konstruktor tidak dapat mengeluarkan pengecualian, seharusnya tidak di coba.
Peringatan ini adalah pemeriksa programmer multi-jalur yang buruk yang telah menyelamatkan saya dari melakukan hal-hal bodoh karena memeriksa setiap jalur dan memastikan bahwa jika Anda menggunakan variabel di beberapa jalur maka Anda harus menginisialisasi di setiap jalur yang mengarah ke sana . Saya sekarang tidak pernah secara eksplisit menginisialisasi variabel sampai saya menentukan bahwa itu adalah hal yang benar untuk dilakukan.
Selain itu, bukankah lebih baik untuk secara eksplisit mengatakan "int size = 0" daripada "int size" dan membuat programmer berikutnya mengetahui bahwa Anda berniat untuk menjadi nol?
Di sisi lain, saya tidak dapat menemukan satu pun alasan yang valid agar kompiler menginisialisasi semua variabel yang tidak diinisialisasi menjadi 0.
sumber
Saya pikir tujuan utamanya adalah untuk mempertahankan kesamaan dengan C / C ++. Namun kompilator mendeteksi dan memperingatkan Anda tentang penggunaan variabel yang tidak diinisialisasi yang akan mengurangi masalah ke titik minimal. Dari perspektif kinerja, ini sedikit lebih cepat untuk membiarkan Anda mendeklarasikan variabel yang tidak diinisialisasi karena compiler tidak perlu menulis pernyataan penugasan, bahkan jika Anda menimpa nilai variabel di pernyataan berikutnya.
sumber
(Mungkin terlihat aneh untuk memposting jawaban baru begitu lama setelah pertanyaan, tetapi muncul duplikat .)
Bagi saya, alasannya begini: Tujuan variabel lokal berbeda dari tujuan variabel instan. Variabel lokal ada untuk digunakan sebagai bagian dari perhitungan; Variabel instance ada di sana untuk memuat status. Jika Anda menggunakan variabel lokal tanpa memberinya nilai, itu hampir pasti merupakan kesalahan logika.
Yang mengatakan, saya benar-benar bisa ketinggalan mengharuskan variabel instan selalu secara eksplisit diinisialisasi; kesalahan akan terjadi pada setiap konstruktor di mana hasilnya memungkinkan variabel contoh yang tidak diinisialisasi (misalnya, tidak diinisialisasi pada deklarasi dan tidak dalam konstruktor). Tapi bukan itu keputusan Gosling, dkk. al., diambil di awal 90-an, jadi di sinilah kita. (Dan saya tidak mengatakan mereka melakukan panggilan yang salah.)
Saya tidak bisa ketinggalan variabel lokal default. Ya, kita seharusnya tidak bergantung pada kompiler untuk memeriksa ulang logika kita, dan yang satu tidak, tetapi masih berguna ketika kompilator menangkapnya. :-)
sumber
Lebih efisien untuk tidak menginisialisasi variabel, dan dalam kasus variabel lokal lebih aman untuk melakukannya, karena inisialisasi dapat dilacak oleh kompilator.
Dalam kasus di mana Anda membutuhkan variabel untuk diinisialisasi, Anda selalu dapat melakukannya sendiri, jadi ini bukan masalah.
sumber
Ide di balik variabel lokal adalah variabel tersebut hanya ada di dalam ruang lingkup terbatas yang dibutuhkannya. Dengan demikian, harus ada sedikit alasan untuk ketidakpastian mengenai nilai, atau setidaknya, dari mana nilai itu berasal. Saya bisa membayangkan banyak kesalahan yang timbul karena memiliki nilai default untuk variabel lokal.
Misalnya, pertimbangkan kode sederhana berikut ... ( NB mari kita asumsikan untuk tujuan demonstrasi bahwa variabel lokal diberi nilai default, seperti yang ditentukan, jika tidak diinisialisasi secara eksplisit )
Ketika semua dikatakan dan dilakukan, dengan asumsi kompilator menetapkan nilai default '\ 0' ke letterGrade , kode ini seperti yang tertulis akan berfungsi dengan baik. Namun, bagaimana jika kita lupa pernyataan lain?
Uji coba kode kami mungkin menghasilkan hal berikut
Hasil ini, meskipun sudah diharapkan, pasti bukan maksud pembuat kode. Memang, mungkin dalam sebagian besar kasus (atau setidaknya, jumlah yang signifikan, daripadanya), nilai default bukanlah nilai yang diinginkan , jadi dalam sebagian besar kasus, nilai default akan menghasilkan kesalahan. Lebih masuk akal untuk memaksa pembuat kode untuk menetapkan nilai awal ke variabel lokal sebelum menggunakannya, karena kesedihan debugging yang disebabkan oleh lupa
= 1
dalamfor(int i = 1; i < 10; i++)
jauh lebih besar daripada kenyamanan karena tidak harus menyertakan= 0
infor(int i; i < 10; i++)
.Memang benar bahwa blok coba-tangkap-akhirnya bisa menjadi sedikit berantakan (tetapi sebenarnya ini bukan tangkapan-22 seperti yang tampaknya disarankan oleh kutipan), ketika misalnya sebuah objek melempar pengecualian yang dicentang dalam konstruktornya, namun untuk satu alasan atau lainnya, sesuatu harus dilakukan untuk objek ini pada akhirnya blok pada akhirnya. Contoh sempurna dari ini adalah ketika berhadapan dengan sumber daya, yang harus ditutup.
Salah satu cara untuk menangani ini di masa lalu mungkin seperti itu ...
Namun, pada Java 7, blok terakhir ini tidak lagi diperlukan menggunakan try-with-resources, seperti itu.
Yang mengatakan, (seperti namanya) ini hanya berfungsi dengan sumber daya.
Dan sementara contoh pertama agak menjijikkan, ini mungkin lebih mengacu pada cara coba-tangkap-akhirnya atau kelas-kelas ini diimplementasikan daripada berbicara tentang variabel lokal dan bagaimana mereka diimplementasikan.
Memang benar bahwa bidang diinisialisasi ke nilai default, tetapi ini sedikit berbeda. Saat Anda mengatakan, misalnya,
int[] arr = new int[10];
segera setelah Anda menginisialisasi larik ini, objek tersebut ada dalam memori di lokasi tertentu. Mari kita asumsikan sejenak bahwa tidak ada nilai default, tetapi nilai awalnya adalah deretan 1 dan 0 apa pun yang kebetulan ada di lokasi memori tersebut saat ini. Hal ini dapat menyebabkan perilaku non-deterministik dalam beberapa kasus.Misalkan kita memiliki ...
Sangat mungkin bahwa
Same.
mungkin ditampilkan dalam satu proses danNot same.
mungkin ditampilkan di proses lain. Masalahnya bisa menjadi lebih menyedihkan setelah Anda mulai berbicara tentang variabel referensi.Menurut definisi, setiap elemen s harus mengarah ke String (atau null). Namun, jika nilai awalnya adalah rangkaian 0 dan 1 apa pun yang terjadi di lokasi memori ini, tidak hanya tidak ada jaminan Anda akan mendapatkan hasil yang sama setiap saat, tetapi juga tidak ada jaminan bahwa objek s [0] menunjuk to (dengan asumsi itu menunjuk ke sesuatu yang berarti) bahkan adalah String (mungkin itu Kelinci,: p )! Kurangnya perhatian terhadap tipe ini akan berhadapan dengan hampir semua yang membuat Java Java. Jadi meskipun memiliki nilai default untuk variabel lokal dapat dilihat sebagai pilihan terbaik, memiliki nilai default untuk variabel instan lebih mendekati kebutuhan .
sumber
jika saya tidak salah, alasan lain bisa jadi
Pemberian nilai default variabel anggota merupakan bagian dari pemuatan kelas
Pemuatan kelas adalah hal yang berjalan dalam java artinya ketika Anda membuat objek maka kelas tersebut dimuat dengan kelas yang memuat hanya variabel anggota yang diinisialisasi dengan nilai default JVM tidak membutuhkan waktu untuk memberikan nilai default ke variabel lokal Anda karena beberapa metode tidak akan pernah dipanggil karena pemanggilan metode bisa bersyarat jadi mengapa butuh waktu untuk memberi mereka nilai default dan mengurangi kinerja jika default tersebut tidak akan pernah digunakan.
sumber
Eclipse bahkan memberi Anda peringatan tentang variabel yang tidak diinisialisasi, jadi itu menjadi cukup jelas. Secara pribadi saya pikir itu adalah hal yang baik bahwa ini adalah perilaku default, jika tidak, aplikasi Anda mungkin menggunakan nilai yang tidak terduga, dan alih-alih kompiler yang membuat kesalahan, ia tidak akan melakukan apa pun (tetapi mungkin memberi peringatan) dan kemudian Anda akan menggaruk kepala Anda tentang mengapa hal-hal tertentu tidak berperilaku sebagaimana mestinya.
sumber
Variabel lokal disimpan di tumpukan, tetapi variabel instan disimpan di heap, jadi ada beberapa kemungkinan bahwa nilai sebelumnya di tumpukan akan dibaca, bukan nilai default seperti yang terjadi di heap. Oleh karena itu, jvm tidak mengizinkan untuk menggunakan variabel lokal tanpa melakukan inisialisasi.
sumber
Variabel instance akan memiliki nilai default tetapi variabel lokal tidak boleh memiliki nilai default. Karena variabel lokal pada dasarnya berada dalam metode / perilaku, tujuan utamanya adalah untuk melakukan beberapa operasi atau kalkulasi. Oleh karena itu, bukanlah ide yang baik untuk menetapkan nilai default untuk variabel lokal. Jika tidak, akan sangat sulit dan memakan waktu untuk memeriksa alasan jawaban yang tidak terduga.
sumber
Jawabannya adalah variabel instan dapat diinisialisasi di konstruktor kelas atau metode kelas apa pun, Tetapi dalam kasus variabel lokal, setelah Anda menentukan apa pun dalam metode yang tetap selamanya di kelas.
sumber
Saya dapat memikirkan 2 alasan berikut
sumber