我正在研究一个小型通用工具,我需要这样的东西:
运营商特征,它将提供操作元素的工具
发布商特征,负责发布以下示例中由Set()表示的结果
将实施操作特征的类
此类的伴随对象,将实现发布者操作。我的设计必须使发布的结果与发布操作保持相同的特性
简而言之,我有以下结构:
trait Publisher[A]{
var storage: Set[A] = Set[A]()
def publishOper(elem: A) = storage += elem
}
trait Operator[A, B]{
def operate(elem: A): B = ???
}
object Oper extends Publisher {
}
class Oper[A, B] extends Operator[A, B]{
def publishOper(elem: A): B = {
val res = operate(elem)
publishOper(res)
}
}
但是,正如您可以想象的那样,我收到以下错误:
publishOper(res):类型不匹配,预期:A,实际:B
这给我提出了几个问题:
扩展伴随对象时,类型推断如何工作? (又名:为什么会这样?)
如何在尝试保持相同结构的同时解决这个问题?
答案 0 :(得分:0)
使用共享发布者对象,可能通过隐式。这实际上更加通用,因为您现在可以控制范围。这也是可测试的,使用伴侣对象作为单身可能不是。
implicit operPub = new Publisher[B] {
// implementation...
}
class Oper[A, B](implicit publisher: Publisher[B]) extends Operator[A, B]{
def publishOper(elem: A): B = {
val res = operate(elem)
publishOper(res)
}
}
val a = new Oper[Int, String]
val b = new Oper[Int, String] // they both should get operPub
答案 1 :(得分:0)
全球发布商是一个坏主意,因为它将是非类型的。但如果你真的需要它 - 只是不要将你的出版商绑定到具体类型:
object Oper extends Publisher[Any]
class Oper[A, B] extends Operator[A, B]{
def publishOper(elem: A): B = {
val res = operate(elem)
Oper.publishOper(res)
res
}
}
但这是一个糟糕的设计。我建议将Oper
定义为具有外部依赖性的特征:
trait Publisher[-A]{ //"-" - if can store Any then can store Int
type T >: A //to compensate covariant position for set or any other your internal providers; implementations without explicit T will produce existential type `_ >: A` for T to guarantee that storage will have a biggest A type
var storage: Set[T] = Set[T]()
def publishOper(elem: A) = storage += elem
}
trait Operator[A, B]{
def operate(elem: A): B = elem.asInstanceOf[B] //just mock
}
trait Oper[A, B] extends Operator[A, B]{
def publisher: Publisher[B]
def publishOper(elem: A): B = {
val res = operate(elem)
publisher.publishOper(res)
res
}
}
示例:
scala> val pbl = new Publisher[Any]{}
pbl: Publisher[Any] = $anon$1@49dbe5f0
scala> class Oper1 extends Oper[Int, Int] { val publisher: Publisher[Int] = pbl }
defined class Oper1
scala> new Oper1
res10: Oper1 = Oper1@4bd282fd
scala> res10.publishOper(3)
res11: Int = 3
scala> res10.publishOper(4)
res12: Int = 4
scala> res10.publisher.storage
res13: Set[res10.publisher.T] = Set(3, 4)
scala> class Oper2 extends Oper[Double, Double] { val publisher: Publisher[Double] = pbl }
defined class Oper2
scala> new Oper2
res14: Oper2 = Oper2@271f68d2
scala> res14.publishOper(2.0)
res15: Double = 2.0
scala> res14.publishOper(3.0)
res16: Double = 3.0
scala> res14.publisher.storage
res17: Set[res14.publisher.T] = Set(3, 4, 2.0)
现在,根据您的具体情况,您可以选择您的发布商可以使用的最大类型(在我的示例中为Any
)。编译器会自动检查你的Oper
是否正常 - 这就是我们在这里需要逆变Publisher
的原因。
P.S。有趣的说明:3.0被自动转换为3,因为预期的存在类型的集合是Int,所以对于我的例子,没有重复Set(3,4, 2.0, 3.0)
但是字符串当然会有所不同:Set(3, 4, 2.0, "3")
:
scala> class Oper3 extends Oper[String, String] { val publisher = pbl }
defined class Oper3
scala> new Oper3
res23: Oper3 = Oper3@f93893c
scala> res23.publishOper("3")
res38: String = 3
scala> res23.publisher.storage
res39: Set[res23.publisher.T] = Set(3.0, 4.0, 2.0, 3)