繁体   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