同じ関数/メソッドでの例外のスローとキャッチ


10

ユーザーが正の整数(自然数)を入力するまで、ユーザーに入力を求める関数を作成しました。誰かが、関数で例外をスローしてキャッチしてはならず、関数の呼び出し元に例外を処理させるべきだと誰かが言った。

他の開発者はこれについてどう思いますか。また、おそらく関数の例外を誤用しています。Javaのコードは次のとおりです。

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;
}

私は2つのことに興味があります。

  1. 発信者に例外について心配させる必要がありますか?この関数のポイントは、ユーザーが自然数を入力するまでユーザーを悩ませることです。機能のポイント悪いですか?私はUI(ユーザーが適切な入力なしにループから抜け出すことができない)について話しているのではなく、例外が処理されたループ入力について話しています。
  2. (この場合)throwステートメントは例外の誤用だと思いますか?数値の妥当性をチェックするフラグを簡単に作成し、そのフラグに基づいて警告メッセージを出力することができました。しかし、それによりコードに行が追加され、そのままでも完全に読みやすいと思います。

事は私がしばしば別の入力関数を書くことです。ユーザーが数値を複数回入力する必要がある場合は、すべてのフォーマット例外と制限を処理する入力用の別の関数を作成します。


言語に大きく依存します。一部の言語では、例外を他の言語よりも自由に使用しています。
マーティンヨーク

回答:


11

例外のポイントは、戻り値にエラーコードを埋め込むことを強制せずに、メソッドが正常に続行できない状態になったことをメソッドに通知できることです。

あなたの場合、メソッドは入力が0以下のときに何をすべきかを正確に知っています。ここで行を保存する唯一の理由は、入力が数値でない場合と同じ例外がスローされるためです。ただし、スローしている例外は、コードが入力を好まない理由を適切に表していません。他の誰かがやって来てこのコードを見た場合、彼らは物事がどのように機能しているかを正確に確認するために余分な時間を費やす必要があります。


ええ、それが誤用だと思ったのはそのためです。したがって、そのthrowステートメントを無視して、呼び出し元に例外をキャッチさせるのではなく、関数内のそのようなループで例外を処理しても問題ないことに同意しますか?Integer.parseInt(ここでも、スローを無視している)のため、catchステートメントが存在します。
usr

1
@usr:その答えは、システムの他の部分がどのように連携して機能するかによります。例外はある時点で処理する必要があります。整理する方法はいくつかありますが、これはその1つです。これは、ここにない他の情報に依存します。
unholysampler 2011

4

これは例外の悪用です。まず、正でない数値はフォーマットの例外ではありません。

なぜ例外を使用するのですか?許可されていない入力がわかっている場合は、次のように、ユーザーから有効な入力が得られるまでループから抜け出さないでください。

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.");
   }
}

Integer.intParseはフォーマット例外をスローするため、catchステートメントの使用は完全に有効です。関数のループでcatchステートメントを使用しても問題ないか、または関数の呼び出し元にフォーマット例外を処理させるかどうかを主に知りたい。
usr

Integer.parseInt()NumberFormatException指定された文字列引数を解析できない場合はをスローします。あなたが自分で例外をスローする必要はありません(そして、あなたはできません)。
バーナード

コード例をより明確にするために編集しました。
バーナード

「そして、あなたは自分で例外を投げることはできません」-そこはどういう意味ですか?
Michael Borgwardt

@Michael Borgwardt:つまり、このInteger.parseInt()メソッドは例外が発生すると例外をスローするため、すでにスローされているため、後で自分でスローすることはできません。
バーナード

1

現在のメソッド呼び出しに関連する何かを行うつもりである場合にのみ、例外をキャッチします。つまり、クリーンアップ、障害ロジックなどです。この場合、catchはコンソールにメッセージを送信するだけで、sideInputメソッドには関係がないため、呼び出しチェーン/スタックの上位で処理できます。

ここでtry / catchを取り除き、メソッド呼び出しを単純に文書化できます。

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

コールチェーン/スタックのさらに上位でその例外を処理する必要があります。


1
メッセージは、より良いUIのためだけにあります。この機能のポイントは、ユーザーが有効な入力を入力するまで、ユーザーに入力を求め続けることです。これは、try-catchブロックなしでは実行できません。私の質問は、ポイント自体が有効であるかどうかでした。私はそうだと思いますが、誰かがtry-catchブロックを削除して、呼び出し側にこの特定の例外を処理させるべきだと言ってきました。ただし、この関数は意図したとおりに機能しません。
usr

1

メソッドで同じ例外をスローしてキャッチすることはできません。catchブロックはスローしているのと同じ例外をキャッチするので、実際にはスローしていません。

parseInt成功した場合、それはではありませんNumberFormatException

サイドがゼロ未満の場合は、をスローする必要がありNegativeSideLengthExceptionます。

という名前のカスタム/ビジネス例外を作成します NegativeSideLengthException

public class NegativeSideLengthException extends Exception
{


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

}

次にsideInput、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;
}

必要に応じて、別のcatchブロックを追加してキャッチNegativeSideLengthExceptionし、メソッドでスローしないようにすることもできます。

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);

フラグは、例外を処理するための良い方法ではありません。


-1

例外はかなり悪夢のようなものであり、解決するよりも複雑になります。

まず、例外をキャッチしない場合、呼び出し元はを実行できますon error resume next。つまり、1週間後でも、関数がスローできるものとそれをどうするかがわからなくなります。

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

基本的に、それらをキャッチする場合、契約と例外保証について非常によく理解する必要があります。それは現実の世界ではめったに起こりません。また、コードが読みにくくなります。

また、面白いのは、本当に例外を処理する可能性がある場合は、本当のRAII言語が必要であることです。これは、Javaと.NETがすべて例外に関するものだからです...

これをもう一度繰り返しますが、...:

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


4
-1は、OPの実際の質問答えるのではなく、完全に正しくない、または少なくともコンテキスト/言語に依存したステートメントでいっぱいの境界線の怒りを投稿します。
ペーテルTörök

@PéterTörök: "人に魚を与え、あなたは彼に1日間餌を与える。人に魚を教えると、あなたは彼に一生餌を与える。" 例外の背後には非常に深刻な問題があり、人々はそれらを知っているべきです-1000 / + 1、私は本当に気にしません。
Coder、

1
例外なく、より簡単に正しいコードを記述できると信じてください。あなたの見解や信念を事実として述べないでください(ジョエルが同じ意見であったとしても、それは事実ではなく意見です)。SEに無関係な回答を投稿しないでください。
ペーテルTörök

@PéterTörök:これは意見ではなく、事実です。内部で例外をスローするコンポーネントを使用するたびに、階層全体をクロールし、コードのすべての1行をチェックして、何をキャッチするかを把握し、コンポーネントが強力であるかどうかを確認する必要があります。保証し、すべてがロールバックされるか、またはそのキャッチが誤った安心感である場合。一体、すべての例外std :: string throwsさえ知らないので、仕様を調べることはできますが、見つけることはできません。のようなもの: throw(thisexception, thatexception)は実に間違っており、使用しないでください。そうしないと、予期しない例外が発生します。
Coder

2
では、例外を使用しないコードはどうでしょうか?ええ、コードをクロールしてすべての行をチェックし、戻り値が適切に処理されているか無視されているかを確認する必要があります。そして、戻り値が無視されると、おそらくアプリが数千行後にクラッシュするまで誰も気付かないでしょう。例外は、少なくともあなたが注意を払うことを強制します。はい、あなたはそれらを飲み込むことができます-しかし、明示的にのみcatch。無視された戻り値は検索できませんが、行ごとのコードレビューでのみ識別できます。最後に重要なことですが、コンストラクターには戻り値がありません。これが例外を使用する主な理由です。
ペーテルTörök
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.