【问题标题】:How to minimally reconcile Scala's @specialized and java.lang.Comparable?如何最低限度地协调 Scala 的 @specialized 和 java.lang.Comparable?
【发布时间】:2018-10-26 12:35:44
【问题描述】:

我有一个第三方 Java API,其方法如下(简化):

public <C extends Comparable<C>> C jFoo(C c);

我想写一个这样的包装器:

def foo[C <: Comparable[C]](c: C): C = jFoo(c)

为了允许包装方法接受原语(因为据我了解,装箱无论如何都会发生,所以这无关紧要),我想专门化C:

def foo[@specialized(Int, Double) C <: Comparable[C]](c: C): C = jFoo(c)

但是,scalac 抱怨说,由于参数的上限,该方法不能被特化。

一种解决方案是添加一个证据参数:

def foo[@specialized(Int, Double) C, CC <: Comparable[CC]](c: C)
                                                 (implicit evComp: C => CC): CC =
    jFoo(evComp(c))

这有效,您现在可以调用:

foo(2.0)

但是,方法签名(API 的一部分)现在并不真正可读。

在方法级别是否有替代方法可以产生等效、更易读的签名,同时保持专业化?

(Scala 2.12.x 很好,Dotty 没那么多)

【问题讨论】:

  • @YuvalItzchakov :问题是Comparable 不是作为类型类设计的——它是为存储状态而设计的,你不能在任何给定的C 上使用它。另外,请注意您需要C &lt;: Comparable[C],not Comparable[C](不幸的是,这不是等效的)。
  • 是的,它实际上也不匹配类型约束。
  • 倒数第二个代码 sn-p 中的 evComp 实际上是什么?
  • @AndreyTyukin:抱歉,错字,已更正。这个隐式由 Scala 的 stdlib BTW 提供,至少对于 Doubles 来说是这样(但我假设其他原语也是如此)。
  • 最后的代码 sn -p foo(2.0) 真的可以编译吗?我出于某种原因得到error: type arguments [Double,Any] do not conform to method foo's type parameter bounds [C,CC &lt;: Comparable[CC]]。看起来好像它无法推断 java.lang.Double 作为第二个参数。

标签: scala comparable


【解决方案1】:

您可以通过将第二个类型参数 CC 设为特殊 EvC 类型类的类型成员来摆脱它。

以下作品:

import java.lang.Comparable

def jFoo[C <: Comparable[C]](c: C): Unit = println("compiles => ship it")

trait EvC[C] {
  type CC <: Comparable[CC]
  def apply(c: C): CC
}

def foo[@specialized(Int, Double) C](c: C)(implicit evC: EvC[C]): Unit = 
  jFoo[evC.CC](evC(c))

implicit object DoubleEvC extends EvC[Double] {
  type CC = java.lang.Double
  def apply(c: Double) = (c: java.lang.Double)
}

foo(42.0) // compiles => ship it

从某种意义上说,我只是将C =&gt; CC 部分与类型参数CC 本身粘合在一起,因此类型CC 不再出现在类型参数列表中。我不确定你是否可以完全摆脱所有这些,内置的原始类型没有实现java.lang.Comparable 仍然是一个事实。

【讨论】:

  • Arghh,我仍然偶尔会忘记依赖类型。这看起来是合法的,谢谢,我明天会测试它(另外,我正在等待其他答案)。关于最后一句话 - 是的,就像我说的,我只需要它,这样我就不必强制到 java.lang.Double (实际签名有点复杂,scalac 无法处理那里的自动转换),拳击无论如何都会发生。我想这是原始 API 的问题,但无论如何我都需要一个解决方案。再次感谢。
  • 在我的具体情况下,我遇到了一些奇怪的编译器错误,但我还是接受了这个答案,因为只要定义了问题,它就可以工作。
  • @mikołak 等等,我的代码会产生编译器错误吗?那不应该。在哪个版本的编译器中会发生这种情况?
  • 您的代码很好 - 这就是我接受答案的原因:)。产生错误的是对原始问题(这个问题是泛化)的解决方案的应用。由于该错误很奇怪并且听起来像一个编译器错误(在“需要X 类型,但找到X”行),我无法隔离问题,并且你的问题是对普遍问题的良好回答,就这个 QA 而言,一切都很好。我可能会在稍后的问题中重新讨论特定的编译器错误。
  • 而且,有趣的是,今天,通过全新的从头重写,它可以工作了。一定搞砸了归属和混淆了编译器(另一种可能性是我在 Java 的 API 类型中添加了一些类型别名,这可能有所帮助)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-05
  • 1970-01-01
  • 2013-11-05
  • 2011-01-01
  • 1970-01-01
  • 2015-04-06
  • 2020-02-17
相关资源
最近更新 更多