【问题标题】:What is the most concise way to construct/build JavaBean objects in Scala?在 Scala 中构造/构建 JavaBean 对象的最简洁方法是什么?
【发布时间】:2011-12-18 16:35:51
【问题描述】:

假设Product 在我无法调整的 Java 库中,因此通过调用 setter 来实例化它:

val product = new Product
product.setName("Cute Umbrella")
product.setSku("SXO-2")
product.setQuantity(5)

我希望能够做到类似这样:

val product = new Product {
  _.setName("Cute Umbrella")
  _.setSku("SXO-2")
  _.setQuantity(5)
}

或者更好:

val product =
  new Product(name -> "Cute Umbrella", sku -> "SXO-2", quantity -> 5)

Scala 可以实现这样的事情吗?

【问题讨论】:

标签: java oop scala builder setter


【解决方案1】:

您可以导入设置器,这样您就不需要限定调用:

val product = {
  val p = new Product
  import p._

  setName("Cute Umbrella")
  setSku("SXO-2")
  setQuantity(5)

  p
}

如果Product 不是最终的,您也可以匿名子类化它并在构造函数中调用setter:

val product = new Product {
  setName("Cute Umbrella")
  setSku("SXO-2")
  setQuantity(5)
}

一旦您尝试使用属性名称和值的 Map 进行实例化,您就会失去静态类型检查,因为您求助于反射。如果您仍然想走这条路,像 Apache Commons BeanUtils 这样的库可以提供帮助。

另一种方法是传递一系列匿名函数来调用每个设置器,并编写一个实用方法将它们应用于对象

def initializing[A](a: A)(fs: (A => Unit)*) = { fs.foreach(_(a)); a }

initializing(new Product)(
  _.setName("Cute Umbrella"),
  _.setSku("SXO-2"),
  _.setQuantity(5)
)

【讨论】:

    【解决方案2】:

    您可以创建一个 Product 对象作为工厂,并在您的 scala.xml 中调用它。像这样的:

    object Product {
      def apply(name: String, sku: String, quantity: Int) = {
         val newProd = new Product()
         import newProd._
         setName(name)
         setSku(sku)
         setQuantity(quantity)
         newProd
    }
    

    然后你就可以完全按照你的意愿使用它了(没有新的)。

       val product = Product(name = "Cute Umbrella", sku = "SXO-2", quantity = 5)
    

    (抱歉,如果上述内容无法编译。在工作中无法访问 Scala :(

    【讨论】:

    • 我认为你需要=而不是->,但这是一个很好的解决方案
    • @Luigi 这就是我复制 OP 代码的结果;)感谢您的更正,我已经编辑了我的回复。
    • 这意味着我必须为每个类创建一个工厂......可能有数十或数百个类......(它仍然可以节省冗余,但希望有一个“更好”,即 Scala 魔法方式)
    • @Hendy:正确。我没有意识到您正在寻找这样一个通用的解决方案。就个人而言,我会使用retronym 的解决方案而不是您选择的解决方案。这两种情况都清楚发生了什么,没有额外的类型/类创建,编译器会告诉你类型错误,或者属性不再存在......
    【解决方案3】:

    我会编写一个隐式转换来使用 Apache Commons BeanUtils

      import org.apache.commons.beanutils.BeanUtils
    
    
      implicit def any2WithProperties[T](o: T) = new AnyRef {
        def withProperties(props: Pair[String, Any]*) = {
          for ((key, value) <- props) { BeanUtils.setProperty(o, key, value) }
          o
        }
      }
    
      test("withProperties") {
        val l = new JLabel().withProperties("background" -> Color.RED, "text" -> "banana")
        l.getBackground should be (Color.RED)
        l.getText should be ("banana")
      }
    

    您不会在编译时获得属性名称或类型检查,但它非常接近您想要的语法,并且通常在创建例如 testdata 时非常有用。


    或者从@retronym 的函数方法中得到启发

      implicit def any2WithInitialisation[T](o: T) = new AnyRef {
        def withInitialisation(fs: (T =>Unit)*) = { 
          fs.foreach(_(o))
          o
        }
      }
    
      test("withInitialisation") {
        val l = new JLabel().withInitialisation(
          _.setBackground(Color.RED), 
          _.setText("banana")
        )
        l.getBackground should be (Color.RED)
        l.getText should be ("banana")
      }
    

    【讨论】:

    • (咆哮)“你在编译时没有得到任何属性名称或类型检查”——这就是我对 java bean 心态的真正困扰。我们有这种静态类型的语言,它可以为您捕捉很多愚蠢的错误,但是,让我们向后弯腰以阻止该语言的内置安全性。此外,让我们基本上将字符串用于所有内容。 ಠ_ಠ (/rant)
    • :-) 但是,嘿,有一些语言已经成功地通过运行时检查开辟了一个利基市场。就我个人而言,我的 Java 和 Scala 测试代码充满了像这样的小技巧,因为我知道如果测试不正确,测试将会失败,并且重视表现力。
    • 虽然我认为丢失类型检查很糟糕,但在我的具体情况下,获得我想要的东西似乎是不可避免的权衡。你的代码只需要编写一次,它适用于任何类。谢谢!
    • 请注意,第二种方法是类型安全的。
    【解决方案4】:

    为了完整起见,如果您需要反过来,从 scala 类中读取 javabean 样式的属性,您可以使用 @BeanProperty@BooleanBeanProperty 注释:

    class MyClass(@BeanProperty foo : String, @BooleanBeanProperty bar : Boolean);
    

    然后从 java:

    MyClass clazz = new MyClass("foo", true);
    
    assert(clazz.getFoo().equals("foo"));
    assert(clazz.isBar());
    

    【讨论】:

      猜你喜欢
      • 2017-12-26
      • 2011-04-10
      • 1970-01-01
      • 2010-09-27
      • 1970-01-01
      • 2019-07-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多