Проблема может быть решена разными способами. Например, если вы просто хотите, чтобы тип T
имел какой-либо метод (и вам все равно, определен ли этот метод для объекта или есть неявное преобразование, которое покрывает объект чем-то, что имеет этот метод), тогда вы можете использовать просмотр границ . Вот пример, который ожидает, что тип T
будет иметь метод def *(times: Int): T
:
class Table[T <% {def *(times: Int): T}](bla: Array[T]) {
bla.foreach( x => println(x * 2))
}
new Table(Array("Hello", "World"))
// Prints:
// HelloHello
// WorldWorld
String
не имеет метода *
, но существует неявное преобразование в StringOps с этим методом.
Вот еще один пример. В этом случае я ограничил тип T
методом def size: Int
:
class Table[T <% {def size: Int}](bla: Array[T]) {
bla.foreach( x => println(x.size))
}
new Table(Array(List(1, 2, 3), List("World")))
// Prints:
// 3
// 1
List
имеет метод size
, и он также работает как ожидалось.
Но это может быть более сложным, если вы работаете с числовыми значениями, такими как int, float, double и т. Д. В этом случае я могу порекомендовать вам использовать context bound . Scala имеет числовой класс типа. Вы можете использовать его для работы с числами, не зная об их типе (с Numeric
вы можете работать с любым, что может быть представлено как число, поэтому ваш код будет гораздо более общим и абстрактным). Вот пример, если это:
import math.Numeric.Implicits._
class Table[T : Numeric](bla: Array[T]) {
bla.foreach( x => println(x * x))
}
new Table(Array(1, 2, 3))
// Prints:
// 1
// 4
// 9
new Table(Array(BigInt("13473264523654723574623"), BigInt("5786785634377457457465784685683746583454545454")))
// Prints:
// 181528856924372945350108280958825119049592129
// 33486887978237312740760811863500355048015109407078304275771413678604907671187978933752066116
Обновление
Как вы отметили в комментариях, Numeric
все еще не решит вашу проблему, потому что он может работать только на числах того же типа. Вы можете просто решить эту проблему, введя новый класс типов. Вот пример этого:
import math.Numeric.Implicits._
trait Convert[From, To] {
def convert(f: From): To
}
object Convert {
implicit object DoubleToInt extends Convert[Double, Int] {
def convert(d: Double): Int = d.toInt
}
implicit object DoubleToBigInt extends Convert[Double, BigInt] {
def convert(d: Double): BigInt = d.toLong
}
}
type DoubleConvert[To] = Convert[Double, To]
class Table[T : Numeric : DoubleConvert](bla: Array[T]) {
bla.foreach( x => println(x * implicitly[DoubleConvert[T]].convert(probability(x))))
def probability(t: T) : Double = t.toDouble + 2.5
}
new Table(Array(1, 2, 3))
new Table(Array(BigInt("13473264523654723574623"), BigInt("5786785634377453434")))
С классом типа DoubleConvert
и контекстом T : Numeric : DoubleConvert
вы не только говорите, что T
должно быть каким-то числом, но также должно существовать некоторое доказательство того, что оно может быть преобразовано из Double
, Вы получаете такое свидетельство с implicitly[DoubleConvert[T]]
, а затем используете его для преобразования Double
в T
. Я определил Convert
для Double -> Int и Double -> BigInt, но вы также можете определить свои собственные для нужных вам типов.