2

私はscalaで本当に奇妙な振る舞いに遭遇しました。エラーが発生しやすいコードと「有効な例外」のリストを引数として取るジェネリックメソッドを作成しました。「有効な例外」がスローされたときにコードを再試行しながら、コードを実行する必要があります。

この方法はうまく機能し、私はいくつかの場所でそれを使用しています。
しかし、その後、メソッド呼び出しの1つがコンパイルに失敗しました。
原因は、例外リストの初期化でした。

他の原因がないことを確認するために、REPLで試してみましたが、結果を自分で確認できます。

me@my-lap:~$ scala -cp /path/to/maven/repository/org/apache/httpcomponents/httpclient/4.1.1/httpclient-4.1.1.jar:/path/to/maven/repository/commons-httpclient/commons-httpclient/3.1/commons-httpclient-3.1.jar:/path/to/maven/repository/me/my-utils/1.0-SNAPSHOT/my-utils-1.0-SNAPSHOT.jar:/path/to/maven/repository/org/apache/httpcomponents/httpcore/4.1/httpcore-4.1.jar
Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_17).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import org.apache.http.conn.{HttpHostConnectException,ConnectTimeoutException}
import org.apache.http.conn.{HttpHostConnectException, ConnectTimeoutException}

scala> import me.util.exceptions.RetryException
import me.util.exceptions.RetryException

scala> val validEx = classOf[ConnectTimeoutException] :: classOf[RetryException] :: classOf[HttpHostConnectException] :: Nil
<console>:9: error: inferred type arguments [java.lang.Class[_ >: _1 with org.apache.http.conn.ConnectTimeoutException <: java.lang.Exception]] do not conform to method ::'s type parameter bounds [B >: java.lang.Class[_ >: org.apache.http.conn.HttpHostConnectException with me.util.exceptions.RetryException <: java.lang.Exception]]
       val validEx = classOf[ConnectTimeoutException] :: classOf[RetryException] :: classOf[HttpHostConnectException] :: Nil
                                                      ^

3つの例外タイプすべてからリストを初期化すると、コードはこの奇妙なエラーで失敗します。だから私はそれらの3からの2つの例外のすべてのサブセットで初期化しようとしました:

scala> val validEx = classOf[ConnectTimeoutException] :: classOf[RetryException] :: Nil
validEx: List[java.lang.Class[_ >: me.util.exceptions.RetryException with org.apache.http.conn.ConnectTimeoutException <: java.lang.Exception]] = List(class org.apache.http.conn.ConnectTimeoutException, class me.util.exceptions.RetryException)

scala> val validEx = classOf[ConnectTimeoutException] :: classOf[HttpHostConnectException] :: Nil
validEx: List[java.lang.Class[_ >: org.apache.http.conn.HttpHostConnectException with org.apache.http.conn.ConnectTimeoutException <: java.io.IOException]] = List(class org.apache.http.conn.ConnectTimeoutException, class org.apache.http.conn.HttpHostConnectException)

scala> val validEx = classOf[RetryException] :: classOf[HttpHostConnectException] :: Nil
validEx: List[java.lang.Class[_ >: org.apache.http.conn.HttpHostConnectException with me.util.exceptions.RetryException <: java.lang.Exception]] = List(class me.util.exceptions.RetryException, class org.apache.http.conn.HttpHostConnectException)

そしてそれはうまくいきました!また、演算子List()を使用する代わりに、3つの例外タイプすべてのリストを作成しようとしました。::そしてそれはまた働いた:

scala> val validEx = List(classOf[ConnectTimeoutException], classOf[RetryException], classOf[HttpHostConnectException])
validEx: List[java.lang.Class[_ >: org.apache.http.conn.HttpHostConnectException with me.util.exceptions.RetryException with org.apache.http.conn.ConnectTimeoutException <: java.lang.Exception]] = List(class org.apache.http.conn.ConnectTimeoutException, class me.util.exceptions.RetryException, class org.apache.http.conn.HttpHostConnectException)

ところで、の実装RetryExceptionは次のとおりです。

class RetryException extends Exception {}

では、なぜそれが起こったのですか?それはscalaの::オペレーターのバグですか?
そして、なぜコンパイラーは:よりも良いタイプを推測できなかったのですか:(List[java.lang.Class[_ >: org.apache.http.conn.HttpHostConnectException with me.util.exceptions.RetryException with org.apache.http.conn.ConnectTimeoutException <: java.lang.Exception]]
私のメソッドはタイプの引数を除いています:validExceptions: List[Class[_ <: java.lang.Throwable]]これははるかに簡潔なタイプです。コンパイラーを除いて次のようなものを理解します:List[Class[_ <: java.lang.Exception]]

(私はScalaバージョン2.9.2(Java7 oracle 1.7.0_17)を搭載したubuntu12.0464ビットで実行しています

4

2 に答える 2

3

これは単に推論の問題のように見えます。

に置き換えNilてみてくださいList.empty[Exception]

val validEx = classOf[ConnectTimeoutException] :: classOf[RetryException] :: classOf[HttpHostConnectException] :: List.empty[Class[_ <: Exception]]

これで修正されるはずです。

次に、元のコードで何が起こるかを見てみましょう。

val validEx = classOf[ConnectTimeoutException] :: classOf[RetryException] :: classOf[HttpHostConnectException] :: Nil

右結合であるため、これは右から左に評価され::ます。


したがって、最初に、コンパイラはを評価しclassOf[HttpHostConnectException] :: Nilます。推測されるタイプはList[Class[HttpHostConnectException]]です。この式の結果を呼び出しましょうtmp1: List[Class[HttpHostConnectException]]


次に、コンパイラはを評価しclassOf[RetryException] :: tmp1ます。型推論は、左側と右側の型を統合しようとし、を思い付きList[_ >: RetryException with HttpHostConnectException]ます。なぜそれが推論しないのか不思議に思うかもしれませんList[Class[Exception]]。これは、Classが共変ではない(不変である)ためClass[HttpHostConnectException]Class[RetryException]であり、のサブタイプではないためですClass[Exception]。ここで、コンパイラーがより単純な(ただし精度は低い)型を推測した可能性があると主張するかもしれませんList[AnyRef]。しかし、コンパイラーは可能な限り正確にしようとします。これは明らかに一般的に非常に便利です。ただし、この場合、次のステップで問題が発生します。tmp2: List[_ >: RetryException with HttpHostConnectException]それでは、この式の結果を呼び出して、次に何が起こるかを見てみましょう。


最後に、コンパイラはを評価しclassOf[ConnectTimeoutException] :: tmp2ます。

ここでは、 (これまでのリストの要素のタイプ)のサブタイプである::マンデートの署名。つまり、との両方を拡張する必要がありますが、これは明らかにそうではないため、コンパイルエラーが発生します。classOf[ConnectTimeoutException]RetryException with HttpHostConnectExceptionConnectTimeoutExceptionRetryExceptionHttpHostConnectException

于 2013-03-06T13:38:41.223 に答える
1

cons演算子(::)を複数回呼び出すと、型推論機能はList各ステップでの正しい型パラメーターを評価しようとします。

短所の最初のカップルの後

classOf[RetryException] :: classOf[HttpHostConnectException] :: Nil

推論されたタイプは、3番目のcons呼び出しと互換性がありません。
エラーメッセージは、上記の値が(リスト要素の)推論されたタイプを持っていることを示しています

java.lang.Class[_ >: org.apache.http.conn.HttpHostConnectException with me.util.exceptions.RetryException <: java.lang.Exception]

したがって、追加の追加要素は上記のスーパータイプである必要があります。

これをコンソールで確認するには、

val intermediateList = classOf[RetryException] :: classOf[HttpHostConnectException] :: Nil

そして、結果の種類を見て、先頭に追加してみてください

classOf[ConnectTimeoutException] :: intermediateList

OPと同じエラーを取得するには


@RégisJean-Gillesが提案したように、作成するList最初の要素が期待されるタイプであることを指定して作成する場合

List.empty[Exception]

コンパイラは問題ないはずです。

オブジェクトファクトリを使用する場合も同じことが起こります。これはList(..., ...)、メソッド呼び出しの型推論が一度に1つのパラメータリスト(パラメータのセット)で実行されるためです。
これは、コンパイラがすべての引数を使用して正しいList型を定義することを意味します

于 2013-03-06T13:51:38.550 に答える