[英]Scala equivalent of Java's Number
我正在嘗試為數值域類型構建一個類型層次結構。 如Year
是一個Int
(這是一個Number
),一個Percentage
是Double
,這是一個Number
,等我需要的層次結構,以便我可以調用toInt
或toDouble
上的值。
但是,原始數字類型的 Scala 類型層次結構除了AnyVal
之外沒有共同的祖先。 這不包含我需要的to{Int, Double}
函數。
我能找到的最接近的類型是Numeric[T]
,它似乎主要存在於某些編譯器技巧中。
在 Java 中,所有從Number
派生的Number
(包括任意精度的數字)。 如何在 Scala 中定義一個滿足數值類型對象的接口?
我目前正在用鴨子打字來破解它:
Any {
def toInt: Int
def toDouble: Double
}
這不僅冗長,而且會產生運行時反射成本。 有什么更好的嗎?
Numeric[T]
正是您要查找的內容。 Scala 的方法是使用類型類(即類似Numeric
的東西)。
代替
def foo(x: java.lang.Number) = x.doubleValue
寫其中之一
def foo[T](x: T)(implicit n: Numeric[T]) = n.toDouble(x)
def foo[T : Numeric](x: T) = implicitly[Numeric[T]].toDouble(x)
其中第二個(幾乎)只是語法糖。
當表達式更復雜時,每次需要操作時編寫對Numeric
實例的調用可能會變得笨拙。 為了減輕這種, Numeric
提供的隱式轉換mkNumericOps
用於擴充T
與寫入的數學運算(即常見方式1 + 2
,而不是n.plus(1,2)
為了使用這些,只需導入隱式Numeric
的成員:
def foo[T](x: T)(implicit n: Numeric[T]) = {
import n._
x.toDouble
}
請注意,由於import
的限制,隱式的縮寫語法在這里幾乎是不可取的。
這里會發生什么? 如果參數列表被標記為implicit
,編譯器將自動將所需類型的值放在那里,如果該類型的值恰好有一個標記為implicit
存在於范圍內。 如果你寫
foo(1.0)
編譯器會自動將其更改為
foo(1.0)(Numeric.DoubleIsFractional)
為方法foo
提供對Double
操作。
這樣做的巨大優勢是您可以在他們不知道的情況下創建Numeric
類型。 假設您有一個為您提供類型MyBigInt
的庫。 現在假設在 Java 世界中——不幸的是——開發人員沒有讓它擴展Number
。 你無能為力。
在 Scala 中,你可以只寫
implicit object MyBigIntIsNumeric extends Numeric[MyBigInt] {
def compare(x: MyBigInt, y: MyBigInt) = ...
// ...
}
並且您使用Numeric
所有代碼現在都可以與MyBigInt
一起MyBigInt
但您不必更改庫。 因此Numeric
甚至可以是您項目的私有數據,並且這種模式仍然有效。
@gzm0 的答案是靜態解決方案,必須在編譯時檢查類型,我給出了一個動態解決方案,在運行時轉換類型,
def toDoubleDynamic(x: Any) = x match { case s: String => s.toDouble case jn: java.lang.Number => jn.doubleValue() case _ => throw new ClassCastException("cannot cast to double") }
它使用 case match 在運行時選擇正確的類型。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.