【问题标题】:Object type inference in Scala while implementing traitsScala 中的对象类型推断,同时实现特征
【发布时间】:2015-02-07 23:04:58
【问题描述】:

我正在开发一个通用的小工具,我需要有这样的东西:

  • Operator trait,将为操作元素提供工具

  • 一个Publisher trait,负责发布一个在以下示例中由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

这给我提出了几个问题:

  1. 扩展伴生对象时类型推断如何工作? (又名:为什么会这样?)

  2. 我该如何解决这个问题,同时尝试保持相同的结构?

【问题讨论】:

  • class Oper[A, B] extends Operator[A, B] with Publisher[B] { ...
  • 那行不通,因为任何类实例都有自己的存储,并且发布不会反映在伴随对象中。这就是为什么我提到设计必须让它们保持相同的特征。
  • 它们不是同一类型。我认为您应该考虑制作一个单独的对象来实现 Publisher,也许是一个隐式的,并将其传递给每个 Oper 实例。
  • class Oper[A,B](implicit publisher: OperPub[B]) extends Operator[A,B]
  • 这几乎限制了解决方案的可扩展性,我无法承受。案例类别(正如您在回复中显示的那样)非常严格。我需要保留类实现。而且,正如所见,我需要从伴随对象访问存储单元。

标签: scala object types traits inference


【解决方案1】:

使用共享发布者对象,可能是通过隐式。这实际上更加通用,因为您现在可以控制范围。这也是可测试的,可能无法将伴生对象用作单例。

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

【讨论】:

    【解决方案2】:

    全局发布者是个坏主意,因为它将是非类型化的。但如果你真的需要它 - 只是不要将你的发布者绑定到具体类型:

    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

    附:有趣的注释: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)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-05-26
      • 1970-01-01
      • 2018-03-21
      • 1970-01-01
      • 2017-10-25
      • 1970-01-01
      • 1970-01-01
      • 2019-12-08
      相关资源
      最近更新 更多