【问题标题】:Force single argument in scala varargs在 scala varargs 中强制使用单个参数
【发布时间】:2012-11-13 10:01:46
【问题描述】:

给定一个带有两个方法的 java 类(取自 mockito):

OngoingStubbing<T> thenReturn(T value);

OngoingStubbing<T> thenReturn(T value, T... values);

如果我从 scala 调用

....thenReturn("something")

我收到一个错误:

Description Resource    Path    Location    Type
ambiguous reference to overloaded definition, both method thenReturn in trait OngoingStubbing of type (x$1: java.lang.Object, x$2: <repeated...>[java.lang.Object])org.mockito.stubbing.OngoingStubbing[java.lang.Object] and  method thenReturn in trait OngoingStubbing of type (x$1: java.lang.Object)org.mockito.stubbing.OngoingStubbing[java.lang.Object] match argument types (java.lang.String)

我不知道如何解决这个问题。

【问题讨论】:

    标签: scala mockito


    【解决方案1】:

    这是一个已知的 Scala-Java 互操作性问题,但很遗憾不在常见问题解答中。这是Scala ticket describing the problem。本质上,当您给出单个参数时,这两种方法都适用,并且 Scala 编译器目前没有任何启发式方法来确定哪个“更具体”。 Alexey Romanov's approach to always use the varargs version 是一个很好的解决方法:

    thenReturn("something", Nil: _*)
    

    还有一个question running into a similar problem with JCommander。那里的答案之一提供了使用结构类型的巧妙解决方法。这种方法将在幕后使用反射,所以你可能想也可能不想去那个方向。对于您的用例,它看起来像:

    type useSingleArgVersion = { def thenReturn(value: AnyRef): OngoingStubbing }
    (...).asInstanceOf[useSingleArgVersion].thenReturn("something")
    

    最后,有一个similar question running into a similar problem with mokito。它并没有真正提供任何解决方法,但它确实更详细地描述了问题,如果您对发生这种情况的原因感兴趣的话。

    【讨论】:

    • 一切正常,直到您返回的是 AnyVal(它不会转换为 java 对象)
    【解决方案2】:

    如果调用可变参数版本是可以接受的,

    thenReturn("something", Nil: _*)
    

    现在想不出一种方法来调用没有可变参数的方法。

    【讨论】:

    • 这对我有用 doReturn(false, Nil: _*) 允许我通过第二个省略号参数的 AnyRef 要求
    【解决方案3】:

    这些答案都是错误的问题。区别很微妙,但这与linked ticket 中的问题不同。那确实需要不合理的体操来调用非可变参数方法。对于这一点,以下就足够了。

    thenReturn[String]("something")
    

    或者,如果您出于某种原因不想这样做,则不需要类型别名和强制转换。您可以直接使用结构类型归属。

    (this: { def thenReturn[T](s: T): OngoingStubbing[T] }).thenReturn("something")
    

    这里的问题是重载和多态交叉处的类型推断——一种方法更具体,但 scalac 不知道是哪一种。由于重载和元组转换之间的相互作用,SI-2991 中的问题是真正的歧义 - 两者都不是更具体的。

    【讨论】:

    • 问题是当你存根一个返回 java.lang.Object(aka AnyRef) 的方法然后你被 Scala 搞砸了。
    • 我认为你的意思是 AnyVal 不是一个对象
    【解决方案4】:

    假设其他人在出现overloaded method value thenReturn with alternatives 错误时会发现这个问题,我也想分享我的解决方案。

    代替

    when(field.getValue(isA(classOf[Record]))).thenReturn(value)

    我用

    doReturn(value).when(field).getValue(isA(classOf[Record]))

    这解决了我的情况的歧义。

    【讨论】:

      【解决方案5】:

      解决方法很简单:

      OngoingStubbing<T> thenReturn(T value);
      
      OngoingStubbing<T> thenReturn(T value1, T valu2, T... values);
      

      没有“可变参数必须为非空”功能。

      【讨论】:

      • 我无法更改底层库。示例方法取自 mockito。
      • 你可以包装底层库或者总是调用第二种方法。正如 Alexey 在另一个答案中所解释的那样。
      【解决方案6】:

      我尝试了 Steve 的解决方案,但遇到了一个巨大的编译器错误,包括:

      scala.tools.nsc.symtab.Types$TypeError: type mismatch;
       found   : scala.reflect.Manifest[Nothing]
       required: scala.reflect.ClassManifest[B]
      Note: Nothing <: B, but trait ClassManifest is invariant in type T.
      You may wish to investigate a wildcard type such as `_ <: B`. (SLS 3.2.10)
      

      我能够使它与类似的东西一起工作:

      thenReturn("something", Seq.empty[Object]: _*)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-10-25
        • 2013-09-30
        • 2021-11-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多