类型参数和数字扩展
type parameters and numeric widening
正如我们所知,我们可以添加 (subtract/multiply/etc.) 两个不同 Numeric
类型的数字,结果将是两种类型中较宽的一个,而不管它们的顺序如何。
33F + 9L // Float + Long == Float
33L + 9F // Long + Float == Float
这是因为每个 7 Numeric
类 (Byte
, Short
, Char
, Int
, Long
, Float
, Double
) 有 7 种不同的 +()
方法(和 -()
, *()
, 等),每个 Numeric
类型一个可以作为传递参数接收。 [有一个额外的 +()
方法来处理 String
参数,但我们在这里不必关心它。]
现在考虑以下问题:
implicit class PlusOrMinus[T: Numeric](a: T) {
import Numeric.Implicits._
def +-(b: T) = if (util.Random.nextBoolean) a+b else a-b
}
如果两个操作数的类型相同,这会起作用,但如果第一个操作数的类型比第二个操作数的类型宽,它也会起作用。
11F +- 2L // result: Float = 9.0 or 13.0
我相信这里发生的事情是编译器在传递给 +-()
方法的第二个操作数(b
参数)上使用 weak conformance to achieve numeric widening。
但是第一个操作数不会被加宽以匹配第二个。它甚至不会编译。
11L +- 2F // Error: type mismatch; found: Float(2.0) required: Long
有什么办法可以解决这个限制吗?
我不能为 b
参数 (def +-[U: Numeric](b: U) = ...
) 使用不同的类型参数,因为通过类型参数表示的 Numeric
只能 add/subtract它是自己的类型。
是用 7 种不同的方法(def +-(b:Short)
、def +-(b:Int)
、def +-(b:Long)
(PlusOrMinusShort/Int/Long/
等)创建 7 个不同的 类(PlusOrMinusShort/Int/Long/
等)的唯一解决方案等等)?
这里有一个方法:
implicit class PlusOrMinus[T: Numeric](a: T) {
import Numeric.Implicits._
def +-(b: T) = plusOrMinus(a,b)
def +-[U: Numeric](b: U)(implicit ev: T => U) = plusOrMinus[U](a,b)
private def plusOrMinus[W: Numeric](a: W, b: W): W =
if (util.Random.nextBoolean) a+b else a-b
}
然后,我得到以下交互:
scala> 11F +- 2L
res0: Float = 9.0
scala> 11L +- 2F
res1: Float = 9.0
我的想法是,如果我可以只拥有一个函数 plusOrMinus
,那么整个问题就会变得微不足道,因为任何一个参数都可能发生相同的扩展。在定义了这样一个函数之后,问题就变成了如何将它嵌入到一个隐式的class中,以在中缀形式中使用它。
在这里,我们只有两种情况:要么第二个参数需要加宽,要么被隐式 class 包裹的参数需要加宽。第一个 +-
方法涵盖了这些情况中的第一个(出于您在上面观察到的原因)。然而,对于第二个,我们需要明确说明存在一些可能的转换并将转换的通用类型传递给 plusOrMinus
.
正如我们所知,我们可以添加 (subtract/multiply/etc.) 两个不同 Numeric
类型的数字,结果将是两种类型中较宽的一个,而不管它们的顺序如何。
33F + 9L // Float + Long == Float
33L + 9F // Long + Float == Float
这是因为每个 7 Numeric
类 (Byte
, Short
, Char
, Int
, Long
, Float
, Double
) 有 7 种不同的 +()
方法(和 -()
, *()
, 等),每个 Numeric
类型一个可以作为传递参数接收。 [有一个额外的 +()
方法来处理 String
参数,但我们在这里不必关心它。]
现在考虑以下问题:
implicit class PlusOrMinus[T: Numeric](a: T) {
import Numeric.Implicits._
def +-(b: T) = if (util.Random.nextBoolean) a+b else a-b
}
如果两个操作数的类型相同,这会起作用,但如果第一个操作数的类型比第二个操作数的类型宽,它也会起作用。
11F +- 2L // result: Float = 9.0 or 13.0
我相信这里发生的事情是编译器在传递给 +-()
方法的第二个操作数(b
参数)上使用 weak conformance to achieve numeric widening。
但是第一个操作数不会被加宽以匹配第二个。它甚至不会编译。
11L +- 2F // Error: type mismatch; found: Float(2.0) required: Long
有什么办法可以解决这个限制吗?
我不能为 b
参数 (def +-[U: Numeric](b: U) = ...
) 使用不同的类型参数,因为通过类型参数表示的 Numeric
只能 add/subtract它是自己的类型。
是用 7 种不同的方法(def +-(b:Short)
、def +-(b:Int)
、def +-(b:Long)
(PlusOrMinusShort/Int/Long/
等)创建 7 个不同的 类(PlusOrMinusShort/Int/Long/
等)的唯一解决方案等等)?
这里有一个方法:
implicit class PlusOrMinus[T: Numeric](a: T) {
import Numeric.Implicits._
def +-(b: T) = plusOrMinus(a,b)
def +-[U: Numeric](b: U)(implicit ev: T => U) = plusOrMinus[U](a,b)
private def plusOrMinus[W: Numeric](a: W, b: W): W =
if (util.Random.nextBoolean) a+b else a-b
}
然后,我得到以下交互:
scala> 11F +- 2L
res0: Float = 9.0
scala> 11L +- 2F
res1: Float = 9.0
我的想法是,如果我可以只拥有一个函数 plusOrMinus
,那么整个问题就会变得微不足道,因为任何一个参数都可能发生相同的扩展。在定义了这样一个函数之后,问题就变成了如何将它嵌入到一个隐式的class中,以在中缀形式中使用它。
在这里,我们只有两种情况:要么第二个参数需要加宽,要么被隐式 class 包裹的参数需要加宽。第一个 +-
方法涵盖了这些情况中的第一个(出于您在上面观察到的原因)。然而,对于第二个,我们需要明确说明存在一些可能的转换并将转换的通用类型传递给 plusOrMinus
.