Scala 如何将特质声明为“内隐”;构造函数参数“;?
我正在设计一个类层次结构,它由一个基类和几个特性组成。基类提供了几种方法的默认实现,traits通过Scala 如何将特质声明为“内隐”;构造函数参数“;?,scala,implicit,traits,Scala,Implicit,Traits,我正在设计一个类层次结构,它由一个基类和几个特性组成。基类提供了几种方法的默认实现,traits通过抽象覆盖选择性地覆盖某些方法,从而充当可堆叠的traits/mixin 从设计的角度来看,这工作得很好,并且映射到域,这样我就可以从这里添加一个过滤函数(一个特征)和一个谓词(另一个特征),等等 但是,现在我想让我的一些特征采用隐式参数。我很高兴,从设计的角度来看,这仍然是有意义的,并且在实践中不会令人困惑。但是,我无法说服编译器使用它运行 问题的核心似乎是我不能为一个特性提供构造函数参数,这样它
抽象覆盖
选择性地覆盖某些方法,从而充当可堆叠的traits/mixin
从设计的角度来看,这工作得很好,并且映射到域,这样我就可以从这里添加一个过滤函数(一个特征)和一个谓词(另一个特征),等等
但是,现在我想让我的一些特征采用隐式参数。我很高兴,从设计的角度来看,这仍然是有意义的,并且在实践中不会令人困惑。但是,我无法说服编译器使用它运行
问题的核心似乎是我不能为一个特性提供构造函数参数,这样它们就可以被标记为隐式。在方法实现中引用隐式参数无法编译,并显示预期的“找不到隐式值”消息;我试图从构造阶段(在实践中,它总是在范围内)将隐式方法“传播”到通过
implicit val e = implicitly[ClassName]
但是(毫无疑问,正如你们中的许多人所期待的那样),这个定义以同样的信息失败了
这里的问题似乎是,我无法说服编译器使用隐式类名
标志标记trait本身的签名,并强制调用方(即将trait混合到对象中的调用方)提供隐式类名称。目前我的调用者正在这样做,但是编译器没有在这个级别进行检查
是否有任何方法将某个特征标记为需要在构建时提供某些隐含信息?
(如果没有,这是不是还没有实现,或者这是不可行的更深层次的原因?事实上,我以前很想这样做,但我刚刚想到了这个想法。你可以翻译
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
}
你可以这样做:
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
的实现,但正如你自己所说,问题的核心是traits不接受构造函数参数——它们是否是隐式的并不重要
此问题的一个可能解决方案是在已经有效的语法中添加一个新功能:
trait A {
implicit val i: Int
}
其中,
i
将由编译器实现,如果一个隐式在作用域中。我遇到过几次这个问题,确实有点烦人,但也不算太多。抽象成员和参数通常是做同一件事的两种可选方法,各有优缺点;对于trait来说,拥有一个抽象成员并不太麻烦,因为您无论如何都需要另一个类来实现trait*
因此,您只需要在trait中有一个抽象值声明,这样实现类就必须为您提供一个隐式值。请参阅下面的示例-该示例编译正确,并显示了实现给定特征的两种方法:
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]
克努特·阿恩·韦达(Knut Arne Vedaa)的回答中也包含了我所展示的基本思想,但我试图给出一个更具说服力和更方便的例子,放弃使用不必要的功能
*这不是trait不能接受参数的原因——我不知道。我只是认为这种限制在这种情况下是可以接受的。因为看起来这是不可能的,我选择在基类的构造函数上声明隐式的
val
。正如问题中所指出的,这并不理想,但它满足了编译器的要求,而且在我的特殊情况下,实际上也没有太大的负担
如果有人有更好的解决方案,我很乐意听到并接受。这是不可能的
但是您可以隐式地使用和Scala的类型推断来尽可能轻松地完成这项工作
trait MyTrait {
protected[this] implicit def e: ClassName
}
然后
class MyClass extends MyTrait {
protected[this] val e = implicitly // or def
}
简洁,甚至不需要在扩展类中编写类型。这是否会使调用程序在匿名对象中显式定义implWrap
,因为它是trait中的抽象字段?(如果不是,我不明白它是如何设置的;你介意解释一下吗?)是的,但请看注释:如果他想使用隐式,他可以只编写val implWrap=ClassNameW
。我没有看到更好的方法:正如你在问题中提到的,traits没有任何构造函数参数(可以标记为隐式)。我做了类似的事情,但使用类型推断来帮助我。但是,通过这种方式,在Base[t]
中定义方法时,您无法访问隐式排序[t]
。如果将numT
设为隐式,则可以解决此问题,但val numT=隐式[Ordering[T]]
将成为一个无限循环。如果修改Base
以解决此问题,则无法编写Der1,但Der2仍然可以工作,并且在等效的情况下仍然比Der1更紧凑<代码>特征基[T]{implicit val numT:ORDINGER[T]}类Der2[T](implicit val numT:ORDINGER[T])扩展基[T]
Scala 2.13:找不到参数e的隐式值