Melempar dan menangkap pengecualian dalam fungsi / metode yang sama

10

Saya telah menulis sebuah fungsi yang meminta input dari pengguna sampai pengguna memasukkan bilangan bulat positif (angka alami). Seseorang berkata saya tidak boleh melempar dan menangkap pengecualian dalam fungsi saya dan harus membiarkan penelepon fungsi saya menangani mereka.

Saya ingin tahu apa yang dipikirkan pengembang lain tentang ini. Saya juga mungkin menyalahgunakan pengecualian dalam fungsi. Berikut kode di Jawa:

private static int sideInput()
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                // probably a misuse of exceptions
                throw new NumberFormatException();
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

Saya tertarik pada dua hal:

  1. Haruskah saya membiarkan penelepon khawatir tentang pengecualian? Maksud dari fungsi ini adalah ia mengganggu pengguna sampai pengguna memasukkan nomor alami. Apakah titik fungsi buruk? Saya tidak berbicara tentang UI (pengguna tidak bisa keluar dari loop tanpa input yang tepat), tetapi tentang input loop dengan pengecualian yang ditangani.
  2. Apakah Anda mengatakan bahwa pernyataan melempar (dalam hal ini) merupakan penyalahgunaan pengecualian? Saya dapat dengan mudah membuat bendera untuk memeriksa validitas nomor dan menampilkan pesan peringatan berdasarkan bendera itu. Tapi itu akan menambah lebih banyak baris ke kode dan saya pikir itu bisa dibaca apa adanya.

Masalahnya, saya sering menulis fungsi input terpisah. Jika pengguna harus memasukkan nomor beberapa kali, saya membuat fungsi terpisah untuk input yang menangani semua pengecualian dan batasan pemformatan.

usr
sumber
Sangat tergantung pada bahasa. Beberapa bahasa menggunakan pengecualian lebih bebas dari yang lain.
Martin York

Jawaban:

11

Inti dari pengecualian adalah memungkinkan metode untuk memberi tahu pemanggil yang memasuki kondisi yang tidak dapat dilanjutkan secara normal, tanpa memaksa Anda untuk menyematkan kode kesalahan pada nilai pengembalian.

Dalam kasus Anda, metode Anda tahu persis apa yang harus dilakukan ketika input tidak lebih besar dari 0. Satu-satunya alasan Anda menyimpan garis di sini adalah karena Anda kebetulan melempar pengecualian yang sama dengan yang akan Anda dapatkan jika inputnya bukan angka. Namun, pengecualian yang Anda lemparkan tidak mewakili dengan benar mengapa kode Anda tidak menyukai input. Jika ada orang lain yang datang dan melihat kode ini, mereka harus meluangkan waktu ekstra untuk mencoba melihat persis bagaimana semuanya berjalan.

unholysampler
sumber
Ya, itu sebabnya saya pikir itu penyalahgunaan. Jadi, mengabaikan pernyataan melempar itu, Anda setuju bahwa tidak apa-apa untuk menangani pengecualian dalam loop semacam itu di dalam fungsi alih-alih membiarkan penelepon menangkapnya? Pernyataan tangkapan ada di sana karena Integer.parseInt (sekali lagi, mengabaikan lemparan).
usr
1
@ Usr: Jawaban itu lebih tergantung pada bagaimana sisa sistem dimaksudkan untuk bekerja bersama. Pengecualian perlu ditangani di beberapa titik. Ada beberapa cara berbeda untuk mengaturnya dan ini adalah salah satunya. Yang terbaik tergantung pada informasi lain yang tidak kami miliki di sini.
unholysampler
4

Ini adalah penggunaan pengecualian yang buruk. Untuk mulai dengan, angka non-positif bukan pengecualian format.

Mengapa menggunakan pengecualian sama sekali? Jika Anda tahu input apa yang tidak diizinkan, jangan sampai keluar dari loop sampai Anda mendapatkan input yang valid dari pengguna, sesuatu seperti yang berikut:

while (true)
{
   // Get user input.
   String input = scanner.nextLine();

   try
   {
      side = Integer.parseInt(input);

      break;
   }
   catch (NumberFormatException ex)
   {
      // Inform user of invalid input.
      System.out.println("Invalid input. Enter a natural number.");
   }
}
Bernard
sumber
Integer.intParse melempar pengecualian format, jadi menggunakan pernyataan catch sangat valid. Saya terutama ingin tahu apakah tidak apa-apa menggunakan pernyataan catch dalam satu loop dalam suatu fungsi atau haruskah saya membiarkan pemanggil fungsi menangani format pengecualian.
usr
Integer.parseInt()melempar a NumberFormatExceptionjika tidak dapat menguraikan argumen string yang disediakan. Tidak perlu bagi Anda (dan Anda tidak akan bisa) untuk melemparkan pengecualian sendiri.
Bernard
Saya telah mengedit contoh kode untuk lebih eksplisit.
Bernard
"dan kamu tidak akan bisa) melemparkan pengecualian sendiri" - apa maksudmu di sana?
Michael Borgwardt
@Michael Borgwardt: Maksud saya karena Integer.parseInt()metode ini akan melempar pengecualian untuk Anda jika itu terjadi, Anda tidak akan bisa melemparkannya sendiri setelah itu karena sudah dibuang.
Bernard
1

Hanya tangkap pengecualian jika Anda bermaksud melakukan sesuatu yang relevan dengan panggilan metode saat ini; yaitu pembersihan, kegagalan logika dll. Dalam hal ini, tangkapan hanya mengirim pesan ke konsol, itu tidak relevan dengan metode sideInput, jadi oleh karena itu dapat ditangani lebih lanjut ke rantai panggilan / tumpukan.

Seseorang dapat menyingkirkan try / catch di sini dan cukup mendokumentasikan pemanggilan metode:

//Throws NumberFormatException if read input is less than 0
private static int sideInput()

Orang masih perlu menangani pengecualian itu lebih lanjut ke rantai panggilan / tumpukan!

Jon Raynor
sumber
1
Pesannya ada hanya untuk UI yang lebih baik. Maksud dari fungsi ini adalah bahwa ia mengomel pengguna untuk input sampai dia memasukkan input yang valid. Ini tidak dapat dilakukan tanpa blok uji coba. Pertanyaan saya adalah apakah intinya sendiri valid. Saya rasa begitu, tetapi seseorang mengatakan kepada saya bahwa saya harus menghapus blok try-catch dan membiarkan penelepon menangani pengecualian khusus ini. Tetapi kemudian fungsi itu tidak akan berfungsi sebagaimana dimaksud.
usr
1

Anda seharusnya tidak melempar dan menangkap pengecualian yang sama dalam suatu metode, saya bahkan berpikir bahwa blok tangkap akan menangkap pengecualian yang sama dengan yang Anda lempar, jadi Anda tidak benar-benar melemparkannya.

Jika parseIntberhasil, maka itu bukan NumberFormatException.

jika sisi kurang dari nol, Anda harus melempar NegativeSideLengthException;

Buat Exception kustom / bisnis bernama NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


    public NegativeSideLengthException(Integer i)
    {
        super("Invalid negative side length "+i);        
    }

}

Kemudian sideInputmelempar NegativeSideLengthException

private static int sideInput() throws NegativeSideLengthException
{
    int side = 0;
    String input;
    Scanner scanner = new Scanner(System.in);

    do {
        System.out.print("Side length: ");
        input = scanner.nextLine();
        try {
            side = Integer.parseInt(input);
            if (side <= 0) {
                throw new NegativeSideLengthException(side);
            }
        }
        catch (NumberFormatException numFormExc) {
            System.out.println("Invalid input. Enter a natural number.");
        }
    } while (side <= 0);

    return side;
}

Anda bahkan dapat (jika Anda mau) menambahkan catch catch block untuk menangkap NegativeSideLengthExceptiondan tidak memiliki metode melemparkannya.

do {
    System.out.print("Side length: ");
    input = scanner.nextLine();
    try {
        side = Integer.parseInt(input);
        if (side <= 0) {
            throw new NegativeSideLengthException(side);
        }
    }
    catch (NumberFormatException numFormExc) {
        System.out.println("Invalid input. Enter a natural number.");
    } catch (NegativeSideLengthException e){
        System.out.println("Invalid input. Enter a non-negative number.");
    }
} while (side <= 0);

Bendera bukan cara yang baik untuk menangani pengecualian.

Tulains Córdova
sumber
-1

Pengecualian adalah hal yang cukup mengerikan, mereka membawa lebih banyak kompleksitas daripada yang mereka pecahkan.

Pertama, jika Anda tidak menangkap pengecualian Anda, penelepon hanya dapat melakukan on error resume next, yaitu, setelah satu minggu, bahkan Anda tidak akan tahu apa fungsi Anda bisa melempar, dan apa yang harus dilakukan dengan itu:

{
    ...
}
catch(OutOfMemory, CorruptedMemory, BadData, DanglingPointers, UnfinishedCommit)
{
    Console.WriteLine("Nothing to see here, move on.");
    Console.WriteLine("The app is very stable, see, no crashing!");
}

Pada dasarnya, jika Anda menangkapnya, Anda harus memiliki pemahaman yang sangat baik tentang kontrak, dan jaminan pengecualian. Itu jarang terjadi di dunia nyata. Dan juga kode Anda akan sulit dibaca.

Juga, lucunya adalah bahwa jika Anda benar-benar memiliki kesempatan menangani pengecualian, Anda memerlukan bahasa RAII yang nyata, yang agak lucu, karena Java dan .NET adalah tentang pengecualian ...

Mengulangi ini sekali lagi, tetapi ...:

http://blogs.msdn.com/b/oldnewthing/archive/2004/04/22/118161.aspx

http://blogs.msdn.com/b/oldnewthing/archive/2005/01/14/352949.aspx

http://www.joelonsoftware.com/items/2003/10/13.html

Coder
sumber
4
-1 untuk memposting kata-kata kasar batas, penuh dengan tidak cukup benar -atau setidaknya tergantung pada konteks / bahasa - pernyataan, alih-alih menjawab pertanyaan aktual OP.
Péter Török
@ PéterTörök: "Berikan seekor ikan kepada seseorang dan Anda memberinya makan selama sehari. Ajari seorang pria untuk memancing dan Anda memberinya makan seumur hidup." Ada masalah yang sangat serius di balik pengecualian, dan orang-orang harus mengetahuinya -1000 / +1, saya tidak begitu peduli.
Coder
1
Jangan ragu untuk percaya Anda dapat menulis kode yang benar lebih mudah tanpa dengan pengecualian. Hanya saja, jangan nyatakan pandangan dan kepercayaan Anda sebagai fakta (bahkan jika Joel memiliki pendapat yang sama, itu masih merupakan opini, bukan fakta), dan jangan posting jawaban yang tidak relevan di SE.
Péter Török
@ PéterTörök: Ini bukan pendapat, ini fakta, setiap kali Anda menggunakan komponen yang melempar pengecualian secara internal, Anda harus merayapi seluruh hierarki dan memeriksa setiap baris kode untuk mengetahui apa yang harus ditangkap, dan jika komponen menawarkan kuat jaminan, dan semuanya dibatalkan, atau jika tangkapan itu hanya rasa aman yang salah. Heck, Anda bahkan tidak tahu semua pengecualian std :: string throws, Anda dapat melihat spesifikasi, tetapi Anda tidak akan pernah menemukan. Hal-hal seperti : throw(thisexception, thatexception)itu benar-benar salah dan tidak boleh digunakan, karena jika tidak, Anda akan mendapatkan pengecualian yang tidak terduga.
Coder
2
OK, jadi bagaimana dengan kode yang tidak menggunakan pengecualian? Astaga, Anda perlu merangkak melalui kode untuk memeriksa setiap baris untuk melihat apakah nilai kembali ditangani dengan benar atau diabaikan. Dan ketika nilai pengembalian diabaikan, tidak ada yang memperhatikan sampai mungkin aplikasi Anda mogok beberapa ribu baris kemudian. Pengecualian setidaknya memaksa Anda untuk memperhatikan. Ya, Anda dapat menelannya - tetapi hanya dengan yang eksplisit catch. Meskipun nilai pengembalian yang diabaikan tidak dapat ditelusuri - ini hanya dapat diidentifikasi melalui peninjauan kode baris-demi-baris. Last but not least, konstruktor tidak memiliki nilai balik, ini adalah alasan utama untuk menggunakan pengecualian.
Péter Török