Scala type mismatch: required _$1 where type _$1 &

2019-08-09 08:23发布

问题:

I'm a newbie to Scala and I'm facing an issue I can't understand and solve. I have written a generic trait which is this one:

trait DistanceMeasure[P<:DbScanPoint] {
  def distance(p1:P, p2:P):Double
}

where DbScanPoint is simply:

trait DbScanPoint extends Serializable {}

Then I have the following two classes extending them:

class Point2d (id:Int, x:Double, y:Double) extends DbScanPoint {
   def getId() = id
   def getX() = x
   def getY() = y
}

class EuclideanDistance extends DistanceMeasure[Point2d] with Serializable {

   override def distance(p1:Point2d,p2:Point2d) = {
      (p1.getX()-p2.getX())*(p1.getX()-p2.getX()) + (p1.getY()-p2.getY()) * (p1.getY()-p2.getY())
  }
}

And at the end I have this class:

class DBScanSettings {

   var distanceMeasure:DistanceMeasure[_<:DbScanPoint] = new EuclideanDistance
   //...
}

My problem is that if that when I write in my test main this:

 val dbScanSettings = new DBScanSettings()
 dbScanSettings.distanceMeasure.distance(new Point2d(1,1,1), new Point2d(2,2,2))

I get the following compiling error:

 type mismatch;
 [error]  found   : it.polito.dbdmg.ontic.point.Point2d
 [error]  required: _$1 where type _$1 <: it.polito.dbdmg.ontic.point.DbScanPoint

I can't understand which is the problem. I have done a very similar thing with other classes and I got no error, so the reason of this error is quite obscure to me.

May somebody help me?

Thanks.

UPDATE

I managed to do what I needed by changing the code to:

trait DistanceMeasure {
  def distance(p1:DbScanPoint, p2:DbScanPoint):Double
}

And obviously making all the related changes.

回答1:

The heart of your problem is that you are defining your distanceMeasure var with an existential type, so to the compiler that type is not completely known. Then, you are calling distance which is to take two instances of type P <: DbScanPoint passing in two Point2d instances. Now, these are the correct types for the concrete class behind distanceMeasure (a new EuclideanDistance), but the way you defined distanceMeasure (with an existential), the compiler cannot enforce that Point2d instances are the right type that the concrete underlying DistanceMeasure takes.

Say for arguments sake that instead of a new EuclideanDistance, you instead instantiated a completely different impl of DistanceMeasure that did not take Point2d instances and then tried to call distance he way you have it here. If the compiler can't enforce that the underlying class accepts the arguments supplied, it's going to complain like this.

There are a bunch of ways to fix this, and the solution ultimately depends on the flexibility you need in your class structure. One possible way is like so:

trait DBScanSettings[P <: DbScanPoint] {
  val distanceMeasure:DistanceMeasure[P]
 //...
}

class Point2dScanSettings extends DBScanSettings[Point2d]{
  val distanceMeasure = new EuclideanDistance
}

And then to test:

val dbScanSettings = new Point2dScanSettings()
dbScanSettings.distanceMeasure.distance(new Point2d(1,1,1), new Point2d(2,2,2))

But without me really understanding you requirements for what levels of abstraction you need, it's going to be up to you to define the restructure.