2016-09-01 13 views
5

Jak wiemy, możemy dodawać (odejmować/mnożyć/itp.) Dwie liczby różnych typów Numeric, a wynikiem będzie szerszy z dwóch typów, niezależnie od ich kolejności.Parametry typu i poszerzenie numeryczne

33F + 9L // Float + Long == Float 
33L + 9F // Long + Float == Float 

Jest tak, ponieważ każda z 7 Numeric klas (Byte, Short, Char, Int, Long, Float, Double) ma 7 różnych +() metody (i -(), *() itp), po jednym dla każdego Numeric Typ które można odebrać jako przekazany parametr. [Nie ma dodatkowy +() metoda obsługi parametru String, ale to nie musi dotyczyć nas tutaj.]

teraz rozważyć następujące kwestie:

implicit class PlusOrMinus[T: Numeric](a: T) { 
    import Numeric.Implicits._ 
    def +-(b: T) = if (util.Random.nextBoolean) a+b else a-b 
} 

To działa, jeśli dwa operandy są tego samego typu, ale działa również, gdy typ pierwszego operandu jest szerszy niż typ drugiego.

11F +- 2L // result: Float = 9.0 or 13.0 

wierzę, co się tutaj dzieje jest to, że kompilator używa weak conformance osiągnąć numeric widening na 2. argumentu (parametr b), jak to jest przekazywane do metody +-().

Ale pierwszy operand nie zostanie poszerzony, aby pasował do drugiego. Nawet się nie skompiluje.

11L +- 2F // Error: type mismatch; found: Float(2.0) required: Long 

Czy jest jakiś sposób obejścia tego ograniczenia?

Nie mogę użyć innego parametru typu dla argumentu b (def +-[U: Numeric](b: U) = ...), ponieważ Numeric, wyrażony za pomocą parametru typu, może tylko dodawać/odejmować własny typ.

jest jedynym rozwiązaniem, aby stworzyć 7 różnych klas (PlusOrMinusShort/Int/Long/ etc.) z 7 różnymi metodami (def +-(b:Short), def +-(b:Int), def +-(b:Long), itd.)?

Odpowiedz

4

Oto sposób:

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 
} 

Następnie, z tym, mam następujące interakcje:

scala> 11F +- 2L 
res0: Float = 9.0 

scala> 11L +- 2F 
res1: Float = 9.0 

Chodzi o to, że gdybym mógł mieć funkcję plusOrMinus, cały ten problem byłby trywialny, ponieważ takie samo poszerzenie mogłoby się zdarzyć w przypadku obu argumentów. Po zdefiniowaniu takiej funkcji problem polega na tym, jak osadzić ją w niejawnej klasie, aby użyć jej w postaci infiksowej.

Tutaj mamy tylko dwa przypadki: albo drugi argument musi zostać poszerzony, albo argument zawinięty przez niejawną klasę musi zostać poszerzony. Pierwszy z tych przypadków jest objęty pierwszą metodą +- (z powodów, które zaobserwowałeś powyżej). Na drugi jednak musimy wyraźnie powiedzieć, że jest jakaś konwersja, która jest możliwa i przekazać ogólny typ konwersji do plusOrMinus.