You could use a type class but you'd lose the possibility of having a type value because this pattern requires the use of an implicit parameter:
import java.nio.{ByteBuffer, ByteOrder}
trait Bufferable[A] {
def size: Int
def put(a: A, buffer: ByteBuffer): Unit
}
implicit val intBufferable: Bufferable[Int] =
new Bufferable[Int] {
override val size = java.lang.Integer.SIZE / 8
def put(n: Int, buffer: ByteBuffer): Unit = buffer.putInt(n)
}
implicit val longBufferable: Bufferable[Long] =
new Bufferable[Long] {
override val size = java.lang.Long.SIZE / 8
def put(n: Long, buffer: ByteBuffer): Unit = buffer.putLong(n)
}
final case class RichSeq[A](seq: Seq[A])(implicit buf: Bufferable[A]) {
def toByteArray: Array[Byte] = {
val buffer = ByteBuffer.allocate(buf.size * seq.length)
buffer.order(ByteOrder.LITTLE_ENDIAN)
seq.foreach(buf.put(_, buffer))
buffer.array()
}
}
RichSeq(Vector(1, 2, 3)).toByteArray.size // evaluates to 12
RichSeq(Vector(1L, 2L, 3L)).toByteArray.size // evaluates to 24
You can play with this code here on Scastie.
If you can, you may probably want to consider the possibility of having a simple helper for this, so that you can avoid unnecessary allocations:
object SeqUtils {
def toByteArray[A](seq: Seq[A])(implicit buf: Bufferable[A]): Array[Byte] = {
val buffer = ByteBuffer.allocate(buf.size * seq.length)
buffer.order(ByteOrder.LITTLE_ENDIAN)
seq.foreach(buf.put(_, buffer))
buffer.array()
}
}
SeqUtils.toByteArray(Vector(1, 2, 3)).size
SeqUtils.toByteArray(Vector(1L, 2L, 3L)).size
The revised example is further available on Scastie.
If you want to know more about type classes there's plenty of material available online, I usually recommend this.