2011-08-08 262 views
33

我正在设计一个类层次结构,它由一个基类和几个特征组成。基类提供了几种方法的默认实现,并且这些特征通过abstract override有选择地覆盖某些方法,以充当可堆叠特征/混合。如何将特征声明为隐式“构造函数参数”?

从设计的角度来看,这工作得很好,并映射到域,这样我可以从这里(一个特征)与谓词从这里(另一个特征)等

增加过滤功能。然而,现在我我喜欢我的一些特征来获取隐式参数。我很高兴这从设计的角度来看仍然有意义,并且在实践中不会令人困惑。但是,我无法说服编译器运行它。

问题的核心似乎是我无法为特征提供构造函数参数,因此它们可能被标记为隐式。在方法实现中引用隐式参数时,无法编译期望的“找不到隐式值”消息;我试图通过

implicit val e = implicitly[ClassName] 

到“传播”从施工阶段(其中,在实践中,它总是在范围内)的隐含的是在方法中可用,但(因为毫无疑问,你们许多人期望的那样)定义以相同的讯息失败。

看来这里的问题是我不能说服编译器用implicit ClassName标志标记特征本身的签名,并强制调用者(即那些将特征混合到对象中的调用者)提供隐式。目前我的呼叫者这样做,但编译器不检查此级别。


有没有什么办法来标记性状为需要一定implicits可在施工时间?

(如果没有的话,就是这根本尚未实现,还是有更深层次的原因,这是不切实际的?)

回答

15

其实,我经常之前想这一点,但只是想出了这个主意。您可以翻译

trait T(implicit impl: ClassName) { 
    def foo = ... // using impl here 
} 

为[编辑:原版本没有其他方法可以访问隐含]

trait T { 
    // no need to ever use it outside T 
    protected case class ClassNameW(implicit val wrapped: ClassName) 

    // normally defined by caller as val implWrap = ClassNameW 
    protected val implWrap: ClassNameW 

    // will have to repeat this when you extend T and need access to the implicit 
    import implWrap.wrapped 

    def foo = ... // using wrapped here 
} 
+0

这是不是让调用者明确定义'implWrap'虽然在匿名对象中,因为它是特征中的抽象字段? (如果没有,我不明白它是如何设置的;你介意解释一下吗?) –

+0

是的,但看到注释:如果他想使用隐式,他可以只写'val implWrap = ClassNameW'。我没有看到一个更好的方法来做到这一点:正如你在问题中提到的,特征没有_any_构造函数参数(可能被标记为隐含的)。 –

+7

当然,我会很高兴看到更好的解决方案。 –

0

你可以做这样的:

abstract class C 

trait A { this: C => 
    val i: Int 
}  

implicit val n = 3 

val a = new C with A { 
    val i = implicitly[Int] 
} 

但我我不确定它是否有任何意义 - 你可以明确地引用隐含的值。

我想你想要的是在实例化中摆脱i的实现,但正如你自己所说,问题的核心是特征不需要构造器参数 - 它们是否隐含或不隐含没关系。

此问题的一个可能的解决方案将是一个新的功能添加到已经有效语法:

trait A { 
    implicit val i: Int 
} 

其中i将被编译器实现的,如果一个隐含的是在范围内。

11

我遇到了这个问题几次,确实有点烦人,但不是太多。抽象成员和参数通常是做同样事情的两种替代方式,有其优点和缺点;对于具有抽象成员的特征不是太不方便,因为无论如何你需要另一个类来实现这个特征。因此,你应该简单地在特征中有一个抽象值声明,这样实现类必须提供一个隐式为你。请看下面的例子 - 它编译正确的,并显示出执行给定性状的方法有两种:

trait Base[T] { 
    val numT: Ordering[T] 
} 
/* Here we use a context bound, thus cannot specify the name of the implicit 
* and must define the field explicitly. 
*/ 
class Der1[T: Ordering] extends Base[T] { 
    val numT = implicitly[Ordering[T]] 
    //Type inference cannot figure out the type parameter of implicitly in the previous line 
} 
/* Here we specify an implicit parameter, but add val, so that it automatically 
* implements the abstract value of the superclass. 
*/ 
class Der2[T](implicit val numT: Ordering[T]) extends Base[T] 

的基本想法,我展示的是也存在于克努特阿恩Vedaa的答案,但我试图让一个更具吸引力和方便例如,放弃使用不需要的功能。

*这不是特质不能接受参数的原因 - 我不知道。我只是认为在这种情况下限制是可以接受的。

+2

但是,通过这种方式,您无法在定义Base [T]中的方法时访问隐式'Ordering [T]'。如果你使'numT'隐式,你修正了_this_问题,但是'val numT =隐式地[Ordering [T]]'变成了一个无限循环。 –

+1

如果修改'Base'来解决这个问题,你不能编写Der1,但是Der2仍然可以工作,并且在等价的情况下比Der1更紧凑。 特性Base [T]隐含val numT:排序[T] } 类Der2 [T](隐式val numT:排序[T])扩展Base [T]' – Blaisorblade

0

因为看起来这是不可能的,所以我选择在基类的构造函数中声明隐含的val。正如在问题中指出的那样,这不是理想的,但它使编译器满意,并且在我的特殊情况下,务实地说,并不是太多的负担。

如果有人有更好的解决方案,但我很乐意听到并接受它。

7

这是不可能的。

但是,您可以使用implicitly和Scala的类型推断来使其尽可能无痛苦。

trait MyTrait { 

    protected[this] implicit def e: ClassName 

} 

然后

class MyClass extends MyTrait { 

    protected[this] val e = implicitly // or def 

} 

简洁,甚至不需要写在扩展类的类型。

相关问题