Kamal Banga
Kamal Banga

Reputation: 107

Instantiating parameterized classes in Scala

I have an Operator abstract class:

abstract class Operator[T, U] {
  def setParent(op: Operator[T, U]): Unit

  def newOp(): Operator[Byte, String] = {
    val newOperator = new NewOperator[Byte, String]
    newOperator.setParent(this)
    newOperator
  }
}

and another NewOperator class

class NewOperator[T, U] extends Operator[T, U] {
  var parent: Operator[T,U] = null
  def setParent(op: Operator[T, U]): Unit = {this.parent = op}
}

Now, in the second line in newOp() method in class Operator, I get an error

newOperator.setParent(this)
                      ^

which says: Type mismatch: expected: Operator[Byte, String], actual: Operator[T, U].

Is the only way to resolve this, is to add .instanceOf[Operator[Byte, String]] to this?

newOperator.setParent(this.instanceOf[Operator[Byte, String]])

Upvotes: 1

Views: 104

Answers (3)

Atiq
Atiq

Reputation: 396

Instead of doing like this

 newOperator.setParent(this) in abstract class operator you can do something like this

 newOperator.setParent(newOperator)

This will solve your problem

Upvotes: 0

dk14
dk14

Reputation: 22374

Your current solution restricts all your Operator[Byte, String] to have Operator[Byte, String] parent type (and you will found it only in runtime if you use asInstanceOf).

In general case if parent/child generic type may be different use:

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Operator[T, U, P <: Operator[_, _, _]] { 
  var parent: P = null.asInstanceOf[P]
  def setParent(op: P): Unit = {this.parent = op}
  def newOp[TT, UU]() = {
    val newOperator = new Operator[TT, UU, Operator[T, U, P]]()
    newOperator.setParent(this)
    newOperator
   }
}


// Exiting paste mode, now interpreting.

defined class Operator

scala> new Operator[Byte, String, Null]
res19: Operator[Byte,String,Null] = Operator@5470e2f4

scala> res19.newOp[Int, String]
res20: Operator[Int,String,Operator[Byte,String,Null]] = Operator@729c1e43

scala> res20.parent
res21: Operator[Byte,String,Null] = Operator@5470e2f4

You may move newOp to some subclass and make Operator a trait, if you need to have some specific state/methods for your Operator.

Or you can use type classes for operator specific operations:

scala> new Operator[Byte, String, Null]
res23: Operator[Byte,String,Null] = Operator@728b49e2

scala> implicit class ByteOperator(o: Operator[Byte, String, _]) {
     |         def hello = "hello" //here you can access some members of Operator
     |     }
defined class ByteOperator

scala> res23.hello
res24: String = hello

If you really need children having same generic as parents:

scala> :paste
// Entering paste mode (ctrl-D to finish)

class Operator[T, U] {
  var parent: Operator[T, U] = null
  def newInstance: Operator[T, U] = new Operator[T, U]
  def newOp: Operator[T, U] = {
      val newOperator = newInstance
      newOperator.setParent(this)
      newOperator
  }
  def setParent(op:  Operator[T, U]): Unit = {this.parent = op}
}


// Exiting paste mode, now interpreting.

defined class Operator

scala> new Operator[Byte, String]
res15: Operator[Byte,String] = Operator@4e6ea769

scala> res15.newOp
res16: Operator[Byte,String] = Operator@c774157

scala> res16.parent
res17: Operator[Byte,String] = Operator@4e6ea769

If you just need to model some AST (Abstract Syntax Tree), case classess may be a good solution:

trait Expression[T] {       
     def v: T
}

case class Value[T](v: T) extends Expression[T] 

case class Plus[T1, T2](a: Expression[T1], b: Expression[T2])(implicit wrap: T1 => Arithmetic[T1, T2]) extends Expression[T1] {
    def v = wrap(a.v) ++ b.v
}

abstract class Arithmetic[T1, T2](v: T1) {
     def ++ (v: T2): T1
}

implicit class ArithmeticInt(val v: Int) extends Arithmetic[Int, Int](v) {
     def ++ (v2: Int) = v + v2
} 

implicit class ArithmeticIntDouble(val v: Int) extends Arithmetic[Int, Double](v) {
     def ++ (v2: Double) = (v.toDouble + v2).toInt
} 

scala> Plus(Value(5.0), Value(11.0)).v
res57: Value[Double] = Value(16.0)

scala> Plus(Value(5), Value(11.0)).v
res67: Int = 16

scala> Plus(Value(5), Value(6)).v
res68: Int = 11

scala> Plus(Value(5.0), Value(6)).v
<console>:60: error: No implicit view available from Double => Arithmetic[Double,Int].
              Plus(Value(5.0), Value(6)).v
                  ^

Upvotes: 1

Septem
Septem

Reputation: 3622

why not make the method newOp generic?

abstract class Operator[T, U] {
  def setParent(op: Operator[T, U]): Unit

  def newOp(): Operator[T, U] = {
    val newOperator = new NewOperator[T, U]
    newOperator.setParent(this)
    newOperator
  }
}

class NewOperator[T, U] extends Operator[T, U] {
  var parent: Operator[T,U] = null
  def setParent(op: Operator[T, U]): Unit = {this.parent = op}
}

val op = new NewOperator[Byte, String]().newOp

Upvotes: 1

Related Questions