在 Scala 中构造/构建 JavaBean 对象的最简洁方法是什么?

发布于 2024-12-12 12:46:58 字数 508 浏览 0 评论 0原文

假设 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 可以实现类似的功能吗?

Let's say Product is in a Java library that I can't tweak, so to instantiate it by calling setters:

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

I'd prefer to be able to do something like this:

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

or better yet:

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

Is something like this possible with Scala ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

寂寞笑我太脆弱 2024-12-19 12:46:58

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

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 这样的库可以提供帮助。

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

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

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

You can import the setters so you don't need to qualify the calls:

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

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

  p
}

If Product isn't final, you could also anonymously subclass it and call the setters in the constructor:

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

As soon as you try to instantiate with a Map of property names and values, you lose static type checking, as you resort to reflection. Libraries like Apache Commons BeanUtils can help out, if you still want to go down that path.

Yet another approach is to pass a sequence of anonymous functions to call each setter, and write a utility method to apply them to the object

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

initializing(new Product)(
  _.setName("Cute Umbrella"),
  _.setSku("SXO-2"),
  _.setQuantity(5)
)
Saygoodbye 2024-12-19 12:46:58

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

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 :(

You could create a Product object as a factory, and call that in your scala. Something like this:

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

and then you can use it exactly as you wanted to (without the new).

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

(apologies if the above doesn't compile. Don't have access to Scala at work :(

∞觅青森が 2024-12-19 12:46:58

我会编写一个隐式转换来使用 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")
  }

您在编译时不会获得属性名称或类型检查,但它非常接近您想要的语法,并且通常在创建测试数据等方面非常有用。


或者从 @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")
  }

I'd write an implicit conversion to use 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")
  }

You don't get property name or type checking at compile time, but it is very close to the syntax that you wanted, and is generally very useful in creating eg testdata.


Or taking a cue from @retronym's function approach

  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")
  }
卖梦商人 2024-12-19 12:46:58

为了完整起见,如果您需要相反的方式,从 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());

For the sake of completeness if you ever need to go the other way around, reading properties in a javabean style from scala classes, you can use @BeanProperty and @BooleanBeanProperty annotations:

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

And then from java:

MyClass clazz = new MyClass("foo", true);

assert(clazz.getFoo().equals("foo"));
assert(clazz.isBar());
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文