我有一个用例,我想做一些像
这样的事情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成为泛型类型(我想避免)。
由于
答案 0 :(得分: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
的新特征(受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)
,所以这很好。