archi1998
archi1998

Reputation: 13

Generic types in Scala

I want to overloading constructor of abstract class with specific type. For example, I have:

abstract class Polinom(v: T)

So, when I create Polinom[String], I want to call constructor(String), for Int I want to call constructor(Int). If T != String and T != Int then I call constructor(T). In addition, for T == Int my object must have method add, for T == String - reverse, for T != String and T != Int object doesn't has this methods. How can I do that?

Upvotes: 1

Views: 98

Answers (3)

user
user

Reputation: 7604

You can use evidence parameters for this:

class Polinom[T](v: T) {

  def add(i: Int)(implicit ev: T <:< Int) = v + i
  def reverse(implicit ev: T <:< String): String = ev(v).reverse

}

val x = new Polinom(50)
val y = new Polinom("hello")
val z = new Polinom(None)

println(x.add(3))
println(y.reverse)
//println(z.add(3)) <- Isn't allowed. Error: Cannot prove that None.type <:< Int

If you want specific constructors for Int and String and can't get enough of that implicit evidence, you can do this:

def this(i: Int)(implicit ev: Int =:= T) = this({
    //do special int stuff here
    println("I am in the special int constructor")
    ev(i)
  })

  def this(s: String)(implicit ev: String =:= T) = this({
    //do special string stuff here
    println("I am in the special string constructor")
    ev(s)
  })

EDIT: Apparently, the above didn't work for the OP, although it seems to be working for me, but you can always make a companion object and overload the apply method, as the other answers suggested.

class Polinom[T] private(v: T)
object Polinom {
  def apply(i: Int): Polinom[Int] = new Polinom(i)
  def apply(s: String): Polinom[String] = new Polinom(s)
  def apply[T](x: T): Polinom[T] = new Polinom(x)
}

val x = Polinom(50)
val y = Polinom("hello")
val z = Polinom(None)

println(x.add(3))
println(y.reverse)
//println(z.add(3)) <- Isn't allowed. Error: Cannot prove that None.type <:< Int

Link to Scastie (the demo's below)

<script src="https://scastie.scala-lang.org/j8fWjvVFS3CumCo3WbUgqQ.js"></script>

Upvotes: 4

Tim
Tim

Reputation: 27356

abstract class Polinom[T](v: T)

object Polinom {
  def apply(i: Int): Polinom[Int] = constructor(i)
  def apply(s: String): Polinom[String] = constructor(s)
  def apply[T](v: T): Polinom[T] = constructor(v)
}

Polinom(0)
Polinom("hello")
Polinom(None)

For examples of the add and reverse methods, see the other answers to this question.

Upvotes: 1

It would be better to model that as an ADT, something like:

sealed trait Polinom[T] {
  def v: T
}

object Polinom {
  final case class IntPolinom private[Polinom] (v: Int) extends Polinom[Int] {
    def add(x: Int): IntPolinom =
      this.copy(v = this.v + x)
  }

  final case class StrPolinom private[Polinom] (v: String) extends Polinom[String] {
    def reverse: StrPolinom =
      this.copy(v = this.v.reverse)
  }

  final case class GenericPolinom[T] private[Polinom] (v: T) extends Polinom[T]

  def apply[T](v: Int): IntPolinom =
    IntPolinom(v)

  def apply[T](v: String): StrPolinom =
    StrPolinom(v)

  def apply[T](v: T): GenericPolinom[T] =
    GenericPolinom(v)
}

Upvotes: 4

Related Questions