簡體   English   中英

scala:classOf [],異常和::運算符,用於列出奇怪的行為

[英]scala: classOf[], exceptions and :: operator for lists weird behavior

我在scala中遇到了一些非常奇怪的行為。 我編寫了一個通用方法,該方法將容易出錯的代碼和“有效異常”列表作為參數,並且應該執行該代碼,同時在拋出“有效異常”時重試該代碼。

該方法很好用,我在幾個地方使用它。
但是然后,其中一個方法調用在編譯時失敗。
原因是異常列表的初始化。

我已經在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種異常類型的List() 它也起作用:

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]] ,該類型的參數為: validExceptions: List[Class[_ <: java.lang.Throwable]] ,這是一種更為簡潔的類型。除了編譯器,我想找出類似的東西: List[Class[_ <: java.lang.Exception]]

(我在Scala版本2.9.2(Java7 oracle 1.7.0_17)的ubuntu 12.04 64bit上運行

這看起來只是一個推斷問題。

嘗試將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 HttpHostConnectException (到目前為止列表元素的類型)的RetryException with HttpHostConnectException的子類型。 換句話說,需要ConnectTimeoutException來擴展RetryExceptionHttpHostConnectException ,這顯然不是這種情況,因此會出現編譯錯誤

當您多次調用cons運算符( :: :)時,類型推斷器將嘗試在每個步驟為您評估List的正確類型參數。

在經過最初的幾個缺點之后

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

推斷的類型與第三個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(..., ...)對象工廠時,也會發生相同的情況,因為對方法調用的類型推斷一次只執行一個參數列表(一組parens)。
這意味着編譯器使用所有參數來定義正確的List類型

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM