【问题标题】:Scala Builder Pattern: illegal cyclic reference involving type TScala Builder 模式:涉及类型 T 的非法循环引用
【发布时间】:2014-06-11 22:20:43
【问题描述】:

我正在尝试为我的用户类层次结构编写一些通用构建器。 我有一个特征,UserBuilder,并且特征中的每个“with”方法都必须返回与当前类相同的类型。因此,如果我在 ComplexUserBuilder 中,withId 方法应该返回 ComplexUserBuilder 而不是 UserBuilder。

但我得到了

涉及类型 T 的非法循环引用

有没有办法解决这个问题?

这是我的代码:

trait UserBuilder[T >: UserBuilder[T]] {

  var id: String = ""

  def withId(id: String): T = {
    this.id = id
    return this
  }
}

class ComplexUserBuilder extends UserBuilder[ComplexUserBuilder] {

  var username: String = ""

  def withUsername(username: String): ComplexUserBuilder = {
    this.username = username
    return this
  }

  def build = new ComplexUser(id, username)
}

顺便说一句,如果我将 trait UserBuilder[T >: UserBuilder[T]] 替换为 trait UserBuilder[T >: UserBuilder[_]] 我会得到:

类型参数 [model.ComplexUserBuilder] 不符合 trait UserBuilder 的类型参数界限 [T >: model.UserBuilder[_]]

更新:

trait UserBuilder[T >: UserBuilder[T]]

应该(正如 GClaramunt 建议的那样)

trait UserBuilder[T <: UserBuilder[T]]

但是现在有一个丑陋的类型作为返回类型

【问题讨论】:

  • 我认为你的方差注释是错误的。不应该是trait UserBuilder[T &lt;: UserBuilder[T]] {吗? (注意 &lt;:&gt;:
  • 你能解释一下你想要达到的目标吗? ComplexUser 类在哪里?
  • ComplexUser 类不相关case class ComplexUser(id: String, username: String)

标签: class scala generics overriding


【解决方案1】:

要让您的UserBuilder 子类引用它们自己的类型,您应该声明this 的类型(并使它们成为sub-types,而不是super-types,UserBuilder):

trait UserBuilder[T <: UserBuilder[T]] { this: T =>
  ...
}

【讨论】:

  • 这不是您最初问题的答案,但我认为让您的构建器对象不可变会更容易更好,withX() 方法会返回新的不可变对象。 b = a.withId(...) 看起来您正在使用不可变对象,而不是 a 会被更改。通过欺骗看到或使用您的代码的人,您可以获得什么?
  • 啊,是的!我已经在 withId 级别尝试过,但忘记将它放在类级别
【解决方案2】:

Dan 已经非常清楚地解决了您的问题。我想说的是,通过使用scala的this.type特性,你甚至根本不需要通用T。

trait UserBuilder {

  var id: String = ""

  def withId(id: String): this.type = {
    this.id = id
    return this
  }
}

class ComplexUserBuilder extends UserBuilder {

  var username: String = ""

  def withUsername(username: String): this.type = {
    this.username = username
    return this
  }

  def build = new ComplexUser(id, username)
}

而且,如果你想将 build 方法添加到 UserBuilder trait,你可能需要添加一个泛型类型 U 来约束用户类型,比如

trait UserBuilder[U <: { def id: String }] {

  var id: String = ""

  def withId(id: String): this.type = {
    this.id = id
    return this
  }

  def build: U
}

class ComplexUserBuilder extends UserBuilder[ComplexUser] {

  var username: String = ""

  def withUsername(username: String): this.type = {
    this.username = username
    return this
  }

  def build = new ComplexUser(id, username)
}

【讨论】:

    【解决方案3】:

    我不确定你想达到什么目的,但你有向后的上限类型限制应该是“<:>”

    :> 是一个下限类型限制,意思是 T 必须是 UserBuilder 的超类型,你想要一个子类型(所以你可以扩展 UserBuilder )

    trait UserBuilder[T <: UserBuilder[T]] {
        self: T =>
    
        var id: String = ""
    
        def withId(id: String): T = {
          this.id = id
          this
        }
      }
    
      class ComplexUserBuilder extends UserBuilder[ComplexUserBuilder] {
    
        var username: String = ""
    
        def withUsername(username: String): ComplexUserBuilder = {
          this.username = username
          this
        }
    
        //def build = new ComplexUser(id, username)
      }
    

    此外,您不需要“返回”,(几乎)Scala 中的所有内容都是返回值的表达式。

    【讨论】:

    • 这似乎可行,但正如你所说,this.asInstanceOf[T] 有点丑...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-09-04
    • 2016-12-23
    • 2011-02-13
    • 1970-01-01
    • 1970-01-01
    • 2019-05-27
    相关资源
    最近更新 更多