【问题标题】:How define Structural Type that the method return this如何定义方法返回的结构类型
【发布时间】:2016-02-24 11:18:54
【问题描述】:

我有许多 Builders 来自库,这些库的源代码是用 Java 自动生成的,超出了我的控制范围。这些Builders 彼此之间没有关联,但它们有许多结构上完全相同的方法。

package a.b

public class Builder {
    public Builder setA(xxx) {}
    public Builder setB(yyy) {}
}

package a.c

public class Builder {
    public Builder setA(xxx) {}
    public Builder setB(yyy) {}
}

使用 Scala 的结构类型,我如何为自己返回构建器?

type StructurallyBuilder = {
    def setA(xxx): StructurallyBuilder
    def setB(yyy): StructurallyBuilder
}

当我想在StructurallyBuilder 上使用 setA 和 setB 时,编译器抱怨它无法解析。

【问题讨论】:

标签: java scala structural-typing


【解决方案1】:

这不是很简单,但我相信您可以使用F-bounded polymorphism 来实现:

type StructurallyBuilder[F <: StructurallyBuilder[F]] = {
  def setA(xxx: Int): F
  def setB(yyy: Int): F
}

在定义采用这些构建器的类或方法时,您必须保留这个复杂的签名。例如:

def setA[T <: StructurallyBuilder[T]](
  xxx: Int, 
  builder: StructurallyBuilder[T]
): T = builder.setA(xxx)

不过貌似可以正常使用这些方法:

val bld: a.c.Builder = setA(10, new a.c.Builder())

【讨论】:

  • 谢谢。这越来越近了。我试过了,但是当我尝试调用setA(10, new a.c.Builder()) 时出现编译器错误。它说“类型不匹配,预期的 StructurallyBuilder [NotInferedT],实际的 a.c.Builder”。如果我输入 setA[a.c.Builder] 它期望 StructurallyBuilder[a.c.Builder]
  • @Wins 嗯,这段代码适用于我:gist.github.com/kolmar/f9df191b8130f948550b 它也适用于具有相同签名的 Java 类:package a.c; public class Builder { public Builder setA(int xxx) { return this; } public Builder setB(int yyy) { return this; } } 也许您的对象与签名不完全对应?跨度>
  • @Wins 另外,Sascha Kolberg 似乎是对的。您甚至不需要 F 有界多态性。另请参阅我的编辑。
  • 感谢它终于起作用了。我在我的代码中错过的是setAsetB 中的参数是另一个Builder 的泛型类型。
  • 您对 F-Bounded polymorphism 的原始答案是正确的。不是后者。
【解决方案2】:

您可以将实际的构建器设为结构类型的类型参数:

import scala.language.reflectiveCalls
import scala.language.existentials

type StructurallyBuilder[T <: AnyRef] = AnyRef {
  def setA(xxx): T
  def setB(yyy): T
}

这是我写的一个小测试,以证明您可以使用它来传递任何使用 'StructurallyBuilder' 作为参数类型的构建器:

import scala.language.reflectiveCalls
import scala.language.existentials

type StructurallyBuilder[T <: AnyRef] = AnyRef {
  def setA(a: Int): T
  def setB(b: String): T
}


class Builder1 {
  var a: Int = _
  var b: String = _

  def setA(a: Int): Builder1 = {
    this.a = a
    this
  }

  def setB(b: String): Builder1 = {
    this.b = b
    this
  }
}

val builder: StructurallyBuilder[_] = new Builder1

val b2 = builder.setA(1)
val b3 = builder.setB("B")

val builder2 = new Builder1

def test(builder: StructurallyBuilder[_]): String = {
  builder.toString
}

val t2 = test(builder2) |-> t2: String = Builder1@7a067558

【讨论】:

  • 它没有按我的需要工作。我需要的是这样你就可以做类似builder.setA(1).setB("B")
【解决方案3】:

为什么不使用 this.type 呢?

type StructurallyBuilder = {
    def setA(x: Int): this.type
    def setB(y: Double): this.type
}

这种用法的例子:

object App
{

  class A {
    def setA(x: Int): this.type = { this }
    def setB(y: Double): this.type = { this }
  }

  type StructurallyBuilder = {
    def setA(x: Int): this.type
    def setB(y: Double): this.type
  }


  def main(args: Array[String]):Unit =
  {
    val a = new A()
    if (a.isInstanceOf[StructurallyBuilder]) {
       System.out.println("qqq")
    }
    System.out.println(a)
   }

}

然后,尝试运行:

[info] Running X.App 
qqq
X.App$A@8f59676

【讨论】:

  • 因为this.type会返回周围StructurallyBuilder的类型。我不想返回拥有StructurallyBuilder 的类,我想返回StructurallyBuilder 本身
猜你喜欢
  • 2022-01-12
  • 2013-03-21
  • 1970-01-01
  • 1970-01-01
  • 2019-12-28
  • 1970-01-01
  • 2012-01-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多