1

我有一个用例,我想做类似的事情

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)。我还检查了其他想法,例如本文的 update2 中描述的方法,但是这里 Self 的协方差不允许定义 bar。

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

谢谢

4

3 回答 3

2

做这样的事情(不引入类型参数)的唯一方法是引入一个abstract type. Foo需要知道它的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启发)的新特征来解决:

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)
于 2013-10-08T21:59:45.087 回答
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.

于 2013-10-08T18:44:09.963 回答
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),所以这很好。

于 2013-10-09T11:05:49.593 回答