简体   繁体   中英

why trait method needs asInstanceOf and class method don't

I have defined the following trait:

trait Felem[T <: Felem[T]] {                                               
  def mul(that: T): T
  def square: T = this.mul(this.asInstanceOf[T])                            
}

I also define a class based on this trait:

class F2elem(val coef: Boolean) extends Felem[F2elem] {
  override def square: F2elem = this.mul(this)
  ...
}

My questions are about the need of "asInstanceOf" in the definition of the "square" method in the trait. If I remove it, I get the following error:

error: type mismatch;
found   : Felem.this.type (with underlying type Felem[T])
required: T
def square: T = this.mul(this)
  1. Why is it needed in the trait ?
  2. Why it is not needed in the class ?
  3. Does it cost anything in term of execution time or memory ?

The parameter of mult must be of type T .

When calling mul(this) , the this parameter is of type Felem[T] , which is not and does not conform to T . There is the additional constraint that T conforms to Felem[T] . But this is not what you want, you would need the opposite, Felem[T] to conform to T .

On the other hand, in F2elem , T is exactly F2elem , so it typechecks (completley unrelated to one being a trait and the other one a class)

Here is example to show that the definition in Felem must indeed not typecheck, and that it is possible to have implementors where Felem[T] does not conform to T .

class F3elem extends Felem[F2elem] // this is 2, not 3

This declaration is correct, F2elem which is given for T satisfies T <: Felem[T] . However, an inherited t his.mul(this) in square would be invalid, mult expect a T , that is F2elem , and this is F3elem . And they are unrelated.

What you probably want is that every Felem must be like F2elem , that is that T must be the type of the actual class. You can enforce this with a self type.

trait Felem [T <: Felem[T]] { this: T => /* your code */ }

When you write that, you state that in every implementation, the type of the implementation must conform to T . Doing that, it will typecheck, and you will not be allowed to instanciate F3elem above :

error: illegal inheritance; self-type F3elem does not conform to Felem[F2elem]'s selftype F2elem class F3elem extends Felem[F2elem] {

1) In your trait this is not an instance of T :

scala> trait Felem[T <: Felem[T]] {
     |   def mul(that: T): T = that
     |   def square: T = this.mul(this.asInstanceOf[T])
     | }
defined trait Felem

scala> class F2elem extends Felem[F2elem]
defined class F2elem

scala> class F3elem extends Felem[F2elem]
defined class F3elem

scala> new F3elem()
res1: F3elem = F3elem@2e0b08f1

scala> res1.square
java.lang.ClassCastException: F3elem cannot be cast to F2elem

2) In your class this is F2elem and T == F2elem , so this is T .

You're using T as your method's parameter type. This means that whatever the type of T is will be the type that's required in the method (hence not needing the cast in the class, since its type is what is signified by T originally).

If you change the type of that to Felem[T] , you will not need the cast.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM