2016-05-25 4 views
2

例外のcatchingやJavaジェネリックのシグネチャに関する予期しない問題が発生しました。さらに騒ぎがなければ、問題のコード(説明は以下の):一般的な引数を持つJavaメソッドcatchブロックの結果から呼び出される

public class StackOverflowTest { 

    private static class WrapperBuilder { 
     public static <T> ResultWrapper of(final T result) { 
      return new ResultWrapper<>(result); 
     } 

     public static ResultWrapper of(final RuntimeException exc) { 
      return new ResultWrapper<>(exc); 
     } 
    } 

    private static class ResultWrapper<T> { 
     private final T result; 
     private final RuntimeException exc; 

     ResultWrapper(final T result) { 
      this.result = result; 
      this.exc = null; 
     } 

     ResultWrapper(final RuntimeException exc) { 
      this.result = null; 
      this.exc = exc; 
     } 

     public Boolean hasException() { 
      return this.exc != null; 
     } 

     public T get() { 
      if (hasException()) { 
       throw exc; 
      } 
      return result; 
     } 

    } 

    private static class WrapperTransformer { 

     public ResultWrapper<Result> getResult(ResultWrapper originalWrappedResult) { 
      if (originalWrappedResult.hasException()) { 
       try { 
        originalWrappedResult.get(); 
       } catch (Exception e) { 
        return WrapperBuilder.of(e); 
       } 
      } 
      return originalWrappedResult; // Transformation is a no-op, here 
     } 
    } 

    private static class Result {} 

    WrapperTransformer wrapper = new WrapperTransformer(); 


    @Test 
    public void testBehaviour() { 
     ResultWrapper wrappedResult = WrapperBuilder.of(new RuntimeException()); 
     final ResultWrapper<Result> result = wrapper.getResult(wrappedResult); 
     assertTrue(result.hasException()); // fails! 
    } 

} 

はともかくは、一瞬のために、悪いスタイルの質問(私は完全にを行うにはより良い方法があることを認めるが、私がやっている何をすべきか!下流のサービスへの呼び出しの結果をラップResultWrapper

  • クラス:ここでは)、これは、次のビジネス・ロジックのトリムダウンと匿名のバージョンがあります。それのいずれかの呼び出し、または得られた例外
  • WrapperTransformerが何らかの方法でResultWrapperを変換するための責任があるクラス(ただし、ここでは、「形質転換」は、何もしません)

試験の結果を含みます上記の失敗は失敗します。デバッグでは、WrapperBuilder.of(e)が実際には汎用メソッド(つまりof(final T result))を呼び出しているため、これが原因であると判断しました。一般的な議論が "欲張り"であれば - RuntimeExceptionで、Tであるので、その方法は賢明な(意図しない)選択である。しかし

DownstreamWrapper::getResult方法がに変更されます。

// i.e. explicitly catch RuntimeException, not Exception 
} catch (RuntimeException e) { 
    return WrapperBuilder.of(e) 
} 

、テストが失敗した - ExceptionRuntimeExceptionとして識別される。すなわち、非ジェネリック.ofメソッドが呼び出され、その結果ResultWrapperが持っています人口はexcです。

これは私にとって完全に難解です。 catch (Exception e)句の中でさえ、eは元のタイプを保持していると思います(ログメッセージSystem.out.println(e.getClass().getSimpleName()はそうだと思います) - キャッチの "タイプ"を変更すると、ジェネリックメソッドのシグネチャがどのように変更されますか?

答えて

3

呼び出されるメソッドは、スタティック引数の型によって定義されます。あなたがExceptionをキャッチした場合

  • 、静的な型はRuntimeExceptionのサブクラスではないので、一般的な of(Object)が呼び出されるException、 です。 (Tは の編集でObjectに翻訳されています)。
  • RuntimeExceptionをキャッチした場合、静的型はRuntimeExceptionであり、of(RuntimeException)に収まるため、より具体的なメソッドが呼び出されます。

e.getClass().getSimpleName()は動的タイプであり、静的タイプではありません。動的型はコンパイル時には不明ですが、呼び出されるメソッドはコンパイル時に選択されます。ここで

は、同じ問題を示していますシンプルなコードである:ここで

public static void foo(Object o) { 
    System.out.println("foo(Object)"); 
} 
public static void foo(Integer n) { 
    System.out.println("foo(Integer)"); 
} 
public static void main (String[] args) throws java.lang.Exception { 
    Number x = new Integer(5); 
    foo(x); 
    System.out.println(x.getClass().getSimpleName()); 
} 

、方法foo(Object)xIntegerであっても、と呼ばれ、コンパイルで知られているxの静的な型、なぜなら時間はNumberであり、Integerのサブクラスではありません。

+0

素晴らしいです、ありがとうございます! – scubbo

関連する問題