如何检查方法参数与此方法的类型相同?

时间:2013-10-08 16:35:46

标签: scala

我有一个用例,我想做一些像

这样的事情
trait Foo {
  def bar[T](x: T)(implicit ev: x.type =:= this.type) = {}
}

所以,当参数x与调用方法的类具有相同的类型时,对bar的调用只会编译。

很清楚,this.type在这种情况下没有帮助,因为每个实例都有不同的this.type,这应该只说明目标。

完整的问题如下:

trait Foo {
  def bar[B <: Foo](o: B) = {} // with some check added
}

abstract class Abstract extends Foo

class Concrete1 extends Abstract

class Concrete2 extends Abstract 

case class Wrapped(a: Abstract)

val c1a = new Concrete1
val c1b = new Concrete1
val c2 = new Concrete2
val ts1 = new Wrapped(new Concrete1)

c1a.bar(c1b) // should compile
ts1.a.bar(c1b) // should also compile
c2.bar(c1b)  // should not compile

使用抽象类型我找到了一个编译c1a.bar(c1b)的解决方案,并没有按预期编译c2.bar(c1b),但也没有编译ts1.a.bar(c1b) 。我还检查了其他一些想法,比如this post的update2中描述的方法,但是这里Self的协方差不允许定义条形。

存在一个我没有看到的解决方案?不使Abstract成为泛型类型(我想避免)。

由于

3 个答案:

答案 0 :(得分:2)

执行此类操作(不引入类型参数)的唯一方法是引入abstract typeFoo需要知道bar方法中的类型:

trait Foo {
  type Self
  def bar[T <: Self](o: T) = {} // with some check added
}

abstract class Abstract extends Foo {
  type Self = Abstract
}

class Concrete1 extends Abstract {
  type Self = Concrete1
}

class Concrete2 extends Abstract {
  type Self = Concrete2
}

这里的问题是您可以轻松地在Self类型上创建一个typeo。这可以通过添加一个名为StrictSelf的新特征(受this question启发)来解决:

trait StrictSelf[T <: StrictSelf[T]] { self: T =>
  type Self >: self.type <: T
}

完整的代码将如下所示:

trait Foo { self:StrictSelf[_] =>
  def bar[T <: Self](o: T) = {}
}

abstract class Abstract extends Foo { self:StrictSelf[_] => }

class Concrete1 extends Abstract with StrictSelf[Concrete1] {
  type Self = Concrete1
}

class Concrete2 extends Abstract with StrictSelf[Concrete2] {
  type Self = Concrete2
}

case class Wrapped[T <: Abstract with StrictSelf[T]](a: T)

在您的情况下,您还可以使用以下(更简单)变体:

trait SelfType[T <: SelfType[T]] { self:T =>
  type Self >: T
}

trait Foo { self:SelfType[_] =>
  def bar(o: Self) = {}
}

abstract class Abstract extends Foo {self: SelfType[_] => }

class Concrete1 extends Abstract with SelfType[Concrete1]

class Concrete2 extends Abstract with SelfType[Concrete2]

case class Wrapped[T <: Abstract](a: T)

答案 1 :(得分:1)

您可以像这样参数化您的特征:

trait Foo[T]{
  def bar(t:T) = {}
}

然后扩展特征的类在扩展时会给出自己的类型:

abstract class Abstract[T] extends Foo[T]

class Concrete1 extends Abstract[Concrete1]
class Concrete2 extends Abstract[Concrete2]
case class Wrapped[T](a:Abstract[T])

这将解决您的问题,但必须在从Foo扩展的每个类上定义类型。

答案 2 :(得分:0)

EECOLOR的答案很有效,并且更接近问题,然后是我在此期间发现自己的替代解决方案。但是这个替代解决方案非常适合我的用例,所以也许对于后来在这个问题上难以接受的其他人来说也是如此。

我将bar移到了一个新的对象:

object Bar {
  def bar[A <: Foo, B <: Foo](a: A, b: B)(implicit ev: A =:= B) = {}
}

并且还使Wrapper类具有通用性:

case class Wrapped[T <: Abstract](a: T)

当然,您必须立即撰写Bar.bar(c1a, c1b)而不是c1a.bar(c1b)。在我的用例Bar.bar转换为Distance.euclidean(c1a, c1b),所以这很好。