[英]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
來擴展RetryException
和HttpHostConnectException
,這顯然不是這種情況,因此會出現編譯錯誤
當您多次調用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.