jts
jts

Reputation: 715

Scala type override

EDITED AFTER TWO ANSWERS BELOW I am trying to make the following Scala code to compile.

  abstract class C {
    type T <: C
    def x(other: T): (T, T)
  }

  class C1 extends C {
    override type T = C1
    override def x(other: C1): (C1, C1) = (this, this)
  }

  def doSthg(cs1 : List[C]) {
    val e1 = cs1(0)
    val e2 = cs1(1)

    e1.x(e2)
  }

But fails with the following message:

Description Resource    Path    Location    Type
type mismatch;  found   : e2.type (with underlying type Chromo[G])  required: e1.C  PI3.sc  /GA/src/org/jts/ga  line 76 Scala Problem
type mismatch;  found   : e2.type (with underlying type org.jts.ga.MI2.C)  required: e1.T   MI2.sc  /GA/src/org/jts/ga  line 18 Scala Problem

Any idea?

Basically, I want to define a generic class like C above, and have methods with the right type on a subclass (C1).

All is good until a generic method is called on C in soSthg.

Thanks

NEW EDIT SECTION

Thanks v much for your replies. Looking at the following code, I was hoping to avoid asInstanceOf.

  abstract class G[T](val t: T) {
    def ~(): G[T]
  }
  abstract class C[T](val g: List[G[T]]) {
    def x(other: C[T]): (C[T], C[T])
  }

  class G1(override val t: Boolean) extends G[Boolean](t){
    override def ~() = new G1(!t)
  }

  class C1(override val g: List[G1]) extends C[Boolean](g) {
    override def x(other: C[Boolean]): (C[Boolean], C[Boolean]) = {
      val go = other.g.map(e => e.asInstanceOf[G1])
      //val go = other.g
      val nc1 = new C1(go)
      (nc1, nc1) // for demo
    }
  }

The signature of x(other: C[Boolean]) is the issue indeed.

This would work:

def doSthg2[T <: C](csl : List[T]) { csl(0).x(csl(1)) }

How to avoid asInstanceOf ?

Upvotes: 1

Views: 211

Answers (2)

Glen Best
Glen Best

Reputation: 23105

There's no problem defining C & C1 as above. The problem is that you're passing an argument to e1.x that is of type C (not C1) and so violates your C1.x method signature.

As far as the compiler is concerned:

  1. You're modelling a type hierarchy rooted at trait/class C
  2. Inside doSthg, the vals e1 & e2 are each of some type within the hierarchy. For e1 & e2, the only things known for sure are that e1.type and e2.type are each subtypes of C. There's no guarantee that e1 and e2 are of the same type (or are subtypes of one another) - even though the class hierarchy is very shallow (just one subclass, C1). You could extend by adding new subtypes of C in new .class/.jar files at any point in the future (possibly generated from .scala/.java files from this compiler on this machine, or even by a different compiler on a different machine!)
  3. You have chosen that your method C1.x is not flexible - it's argument must be of type C1, not any C. Hence, e1.x cannot take an 'arbitrary' e2 <: C as argument.

So the compiler is doing the right thing, based on your design decisions. Solutions:

  • allow C1.x to take arguments of type C
  • make doSthg argument List[C1] (possibly moving doSthg within C1 at the same time)

Upvotes: 1

senia
senia

Reputation: 38045

C1 compiles fine. Note that you could remove override keyword here:

class C1 extends C {
  type T = C1
  def x(other: C1): (C1, C1) = (this, this) // do you mean (other, this)?
}

doSthg is just invalid: you can't prove that e2 is e1.T. This works as expected:

def doSthg(e1: C)(e2: e1.T) {
  e1.x(e2)
}

Or this:

abstract class C {
  ...
  def create(): T
}

def doSthg(e1: C) {
  val e2 = e1.create()
  e1.x(e2)
}

For your original doSthg method x should accept any instance of C:

trait C {
  def x(other: C): (C, C)
}

Upvotes: 10

Related Questions