Saya mengalami situasi di mana metode non-void kehilangan pernyataan kembali dan kode masih dikompilasi. Saya tahu bahwa pernyataan setelah loop sementara tidak dapat dijangkau (kode mati) dan tidak akan pernah dieksekusi. Tetapi mengapa kompiler bahkan tidak memperingatkan tentang mengembalikan sesuatu? Atau mengapa suatu bahasa memungkinkan kita untuk memiliki metode non-void yang memiliki loop tak terbatas dan tidak mengembalikan apa pun?
public int doNotReturnAnything() {
while(true) {
//do something
}
//no return statement
}
Jika saya menambahkan pernyataan break (bahkan yang kondisional) di loop sementara, kompiler mengeluh tentang kesalahan terkenal: Method does not return a value
di Eclipse dan Not all code paths return a value
di Visual Studio.
public int doNotReturnAnything() {
while(true) {
if(mustReturn) break;
//do something
}
//no return statement
}
Ini berlaku untuk Java dan C #.
unreachable statement
jika ada sesuatu setelah loop.Jawaban:
Aturan untuk metode non-void adalah setiap jalur kode yang mengembalikan harus mengembalikan nilai , dan aturan itu puas dalam program Anda: nol dari jalur kode nol yang kembali memang mengembalikan nilai. Aturannya bukan "setiap metode non-void harus memiliki jalur kode yang mengembalikan".
Ini memungkinkan Anda untuk menulis metode rintisan seperti:
Itu metode yang tidak berlaku. Itu harus menjadi metode non-void untuk memenuhi antarmuka. Tetapi tampaknya konyol untuk membuat implementasi ini ilegal karena tidak mengembalikan apa pun.
Bahwa metode Anda memiliki titik akhir yang tidak terjangkau karena
goto
(ingat, awhile(true)
adalah cara yang lebih menyenangkan untuk menulisgoto
) daripadathrow
(yang merupakan bentuk lain darigoto
) tidak relevan.Karena kompiler tidak memiliki bukti yang baik bahwa kode tersebut salah. Seseorang menulis
while(true)
dan sepertinya orang yang melakukan itu tahu apa yang mereka lakukan.Lihat artikel saya tentang masalah ini, di sini:
ATBG: reachability de facto dan de jure
Dan Anda mungkin juga mempertimbangkan membaca spesifikasi C #.
sumber
public int doNotReturnAnything() {
boolean flag = true;
while (flag) {
//Do something
}
//no return
}
Kode ini juga tidak memiliki titik istirahat. Jadi sekarang bagaimana kompiler mengetahui kode itu salah.if
,while
,for
,switch
, dll, bercabang konstruksi yang beroperasi pada konstanta diperlakukan sebagai cabang tanpa syarat oleh compiler. Definisi tepat dari ekspresi konstan ada di spec. Jawaban atas pertanyaan Anda ada dalam spesifikasi; saran saya adalah Anda membacanya.Every code path that returns must return a value
Jawaban Terbaik. Jelas menjawab kedua pertanyaan itu. Terima kasihKompiler Java cukup pintar untuk menemukan kode yang tidak terjangkau (kode setelah
while
putaran)dan karena tidak dapat dijangkau , tidak ada gunanya menambahkan
return
pernyataan di sana (setelahwhile
berakhir)hal yang sama berlaku dengan kondisional
if
karena kondisi boolean
someBoolean
hanya dapat mengevaluasi salah satutrue
ataufalse
, tidak perlu memberikan secarareturn
eksplisit setelahif-else
, karena kode itu tidak dapat dijangkau , dan Java tidak mengeluh tentang hal itu.sumber
return
pernyataan dalam kode yang tidak dapat dijangkau, tetapi tidak ada hubungannya dengan mengapa Anda tidak memerlukan pernyataan apa punreturn
dalam kode OP.public int doNotReturnAnything() {
if(true){
System.exit(1);
}
return 11;
}
public int doNotReturnAnything() {
while(true){
System.exit(1);
}
return 11;// Compiler error: unreachable code
}
System.exit(1)
akan mematikan program. Itu hanya dapat mendeteksi jenis kode tertentu yang tidak terjangkau.System.exit(1)
akan mematikan program, kita bisa menggunakan apa sajareturn statement
, sekarang kompiler mengenalireturn statements
. Dan perilakunya sama, pengembalian membutuhkanif condition
tetapi tidak untukwhile
.while(true)
Compiler tahu bahwa
while
loop tidak akan pernah berhenti dieksekusi, maka metode ini tidak akan pernah selesai, makareturn
pernyataan tidak diperlukan.sumber
Mengingat loop Anda dieksekusi pada sebuah konstanta - kompiler tahu bahwa itu adalah infinite loop - artinya metode ini tidak akan pernah bisa kembali.
Jika Anda menggunakan variabel - kompiler akan menegakkan aturan:
Ini tidak dapat dikompilasi:
sumber
Spesifikasi Java mendefinisikan konsep yang disebut
Unreachable statements
. Anda tidak diizinkan memiliki pernyataan yang tidak terjangkau dalam kode Anda (ini adalah kesalahan waktu kompilasi). Anda bahkan tidak diizinkan memiliki pernyataan pengembalian setelah beberapa saat (benar); pernyataan di Jawa. Sebuahwhile(true);
pernyataan yang membuat pernyataan berikut tidak terjangkau oleh definisi, karena itu Anda tidak perlureturn
pernyataan.Perhatikan bahwa sementara Menghentikan masalah tidak dapat diputuskan dalam kasus umum, definisi Unreachable Statement lebih ketat daripada hanya berhenti. Ini memutuskan kasus yang sangat spesifik di mana suatu program pasti tidak berhenti. Compiler secara teori tidak dapat mendeteksi semua loop tak terhingga dan pernyataan yang tidak dapat dijangkau tetapi ia harus mendeteksi case spesifik yang didefinisikan dalam spesifikasi (misalnya
while(true)
case)sumber
Kompiler cukup pintar untuk mengetahui bahwa
while
loop Anda tidak terbatas.Jadi kompiler tidak dapat berpikir untuk Anda. Tidak dapat menebak mengapa Anda menulis kode itu. Yang sama berarti nilai-nilai kembali metode. Java tidak akan mengeluh jika Anda tidak melakukan apa pun dengan nilai pengembalian metode.
Jadi, untuk menjawab pertanyaanmu:
Kompiler menganalisis kode Anda dan setelah mengetahui bahwa tidak ada jalur eksekusi yang mengarah ke akhir fungsi yang selesai dengan OK.
Mungkin ada alasan yang sah untuk loop tak terbatas. Misalnya banyak aplikasi menggunakan loop utama tanpa batas. Contoh lain adalah server web yang mungkin menunggu permintaan tanpa batas.
sumber
Dalam teori tipe, ada sesuatu yang disebut tipe dasar yang merupakan subkelas dari setiap tipe lainnya (!) Dan digunakan untuk menunjukkan non-terminasi di antara hal-hal lain. (Pengecualian dapat dihitung sebagai jenis non-pemutusan - Anda tidak berhenti melalui jalur normal.)
Jadi dari perspektif teoretis, pernyataan-pernyataan ini yang non-terminating dapat dianggap untuk mengembalikan sesuatu dari tipe Bawah, yang merupakan subtipe dari int, sehingga Anda (jenis) mendapatkan nilai pengembalian Anda setelah semua dari perspektif jenis. Dan benar-benar oke bahwa tidak masuk akal bahwa satu jenis dapat menjadi subkelas dari semua yang lain termasuk int karena Anda tidak pernah benar-benar mengembalikan satu.
Dalam kasus apa pun, melalui teori tipe eksplisit atau tidak, kompiler (penulis kompiler) mengakui bahwa meminta nilai kembali setelah pernyataan non-terminasi adalah konyol: tidak ada kasus yang memungkinkan di mana Anda memerlukan nilai itu. (Mungkin bagus jika kompiler Anda memperingatkan Anda ketika tahu ada sesuatu yang tidak akan berakhir tetapi sepertinya Anda ingin mengembalikan sesuatu. Tapi itu lebih baik bagi style-checker ala la lint, karena mungkin Anda memerlukan jenis tanda tangan yang cara untuk beberapa alasan lain (misalnya subclassing) tetapi Anda benar-benar ingin non-terminasi.)
sumber
Tidak ada situasi di mana fungsi dapat mencapai tujuannya tanpa mengembalikan nilai yang sesuai. Oleh karena itu, tidak ada kompiler yang dapat dikeluhkan.
sumber
Visual studio memiliki mesin pintar untuk mendeteksi jika Anda telah mengetik jenis kembali maka harus memiliki pernyataan kembali dengan fungsi / metode.
Seperti dalam PHP, tipe pengembalian Anda benar jika Anda belum mengembalikan apa pun. compiler dapatkan 1 jika tidak ada yang kembali.
Sampai disini
Compiler tahu bahwa sementara pernyataan itu sendiri memiliki sifat infinte sehingga tidak mempertimbangkannya. dan kompiler php akan secara otomatis menjadi kenyataan jika Anda menulis suatu kondisi dalam ekspresi while.
Tetapi tidak dalam kasus VS itu akan mengembalikan Anda kesalahan dalam tumpukan.
sumber
Loop sementara Anda akan berjalan selamanya dan karenanya tidak akan keluar sementara; itu akan terus dijalankan. Karenanya, bagian luar sementara {} tidak dapat dijangkau dan tidak ada gunanya secara tertulis kembali atau tidak. Kompiler cukup cerdas untuk mengetahui bagian mana yang dapat dijangkau dan bagian mana yang tidak.
Contoh:
Kode di atas tidak akan dikompilasi, karena mungkin ada kasus bahwa nilai variabel x dimodifikasi di dalam tubuh while loop. Jadi ini membuat bagian luar dari sementara loop dapat dijangkau! Dan karenanya kompiler akan melempar kesalahan 'tidak ada pernyataan kembali ditemukan'.
Kompiler tidak cukup pintar (atau lebih tepatnya malas;)) untuk mengetahui apakah nilai x akan dimodifikasi atau tidak. Semoga ini membersihkan semuanya.
sumber
int x = 1; while(x * 0 == 0) { ... }
itu adalah infinite loop, tetapi spesifikasinya mengatakan bahwa compiler hanya boleh membuat pengurangan aliran kontrol ketika ekspresi loop konstan , dan spesifikasi mendefinisikan ekspresi konstan sebagai tidak mengandung variabel . Karena itu kompilernya terlalu pintar . Dalam C # 3 saya membuat kompiler cocok dengan spesifikasi, dan sejak itu sengaja kurang pintar tentang ekspresi semacam ini."Mengapa kompiler bahkan tidak memperingatkan tentang mengembalikan sesuatu? Atau mengapa suatu bahasa memungkinkan kita untuk memiliki metode non-void yang memiliki infinite loop dan tidak mengembalikan apa pun?".
Kode ini juga berlaku dalam semua bahasa lain (mungkin kecuali Haskell!). Karena asumsi pertama adalah kita "sengaja" menulis beberapa kode.
Dan ada beberapa situasi di mana kode ini bisa benar-benar valid seperti jika Anda akan menggunakannya sebagai utas; atau jika mengembalikan
Task<int>
, Anda dapat melakukan beberapa pengecekan kesalahan berdasarkan nilai int yang dikembalikan - yang tidak boleh dikembalikan.sumber
Saya mungkin salah tetapi beberapa debugger memungkinkan modifikasi variabel. Di sini, sementara x tidak dimodifikasi oleh kode dan itu akan dioptimalkan oleh JIT orang mungkin memodifikasi x ke false dan metode harus mengembalikan sesuatu (jika hal seperti itu diizinkan oleh C # debugger).
sumber
Spesifik kasus Java untuk ini (yang mungkin sangat mirip dengan kasus C #) berkaitan dengan bagaimana kompiler Java menentukan apakah suatu metode dapat kembali.
Secara khusus, aturannya adalah bahwa metode dengan tipe pengembalian tidak harus dapat diselesaikan secara normal dan sebaliknya harus selalu lengkap secara tiba-tiba (tiba-tiba di sini ditunjukkan melalui pernyataan pengembalian atau pengecualian) per JLS 8.4.7 .
Kompilator terlihat untuk melihat apakah penghentian normal dimungkinkan berdasarkan aturan yang didefinisikan dalam JLS 14.21 Pernyataan Tidak Terjangkau karena juga mendefinisikan aturan untuk penyelesaian normal.
Khususnya, aturan untuk pernyataan yang tidak terjangkau membuat kasus khusus hanya untuk loop yang memiliki
true
ekspresi konstan yang didefinisikan :Jadi jika
while
pernyataan tersebut dapat diselesaikan secara normal , maka pernyataan kembali di bawah ini diperlukan karena kode dianggap dapat dicapai, dan setiapwhile
loop tanpa pernyataan break yang dapat dicapai atautrue
ekspresi konstan dianggap mampu menyelesaikan secara normal.Aturan-aturan ini berarti bahwa Anda
while
pernyataan dengan ekspresi yang benar konstan dan tanpabreak
yang tidak pernah dianggap untuk menyelesaikan normal , dan sehingga setiap kode di bawah itu tidak pernah dianggap dicapai . Akhir dari metode berada di bawah loop, dan karena segala sesuatu di bawah loop tidak dapat dijangkau, demikian juga akhir dari metode, dan dengan demikian metode tersebut tidak mungkin dapat diselesaikan secara normal (yang dicari oleh pengumpul).if
pernyataan, di sisi lain, tidak memiliki pengecualian khusus tentang ekspresi konstan yang diberikan untuk loop.Membandingkan:
Dengan:
Alasan untuk pembedaan ini cukup menarik, dan karena keinginan untuk mengizinkan flag kompilasi bersyarat yang tidak menyebabkan kesalahan kompiler (dari JLS):
Mengapa pernyataan break bersyarat menghasilkan kesalahan kompilator?
Seperti dikutip dalam aturan reachability loop, loop sementara juga bisa selesai secara normal jika berisi pernyataan break yang dapat dicapai. Karena aturan untuk keterjangkauan suatu
if
pernyataan maka klausa tidak mempertimbangkan kondisi ituif
sama sekali, maka klausaif
pernyataan bersyarat seperti itu selalu dianggap dapat dijangkau.Jika
break
terjangkau, maka kode setelah loop sekali lagi juga dianggap dapat dijangkau. Karena tidak ada kode yang dapat dijangkau yang menghasilkan penghentian mendadak setelah loop, metode ini kemudian dianggap dapat menyelesaikan secara normal, dan kompilator menandainya sebagai kesalahan.sumber