Какой самый краткий способ конструировать / строить объекты JavaBean в Scala? - PullRequest
4 голосов
/ 31 октября 2011

Допустим, Product находится в библиотеке Java, которую я не могу настроить, поэтому для создания экземпляра, вызвав сеттеры:

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?

Ответы [ 4 ]

6 голосов
/ 31 октября 2011

Вы можете создать объект 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 на работе: (

6 голосов
/ 31 октября 2011

Вы можете импортировать сеттеры, чтобы вам не нужно было квалифицировать вызовы:

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

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

  p
}

Если Product не является окончательным, вы также можете анонимно создать его подкласс и вызвать сеттеры в конструкторе:

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

Как только вы пытаетесь создать экземпляр с помощью карты имен и значений свойств, вы теряете статическую проверку типов, поскольку прибегаете к отражению. Такие библиотеки, как 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)
)
1 голос
/ 31 октября 2011

Я бы написал неявное преобразование для использования 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")
  }
1 голос
/ 31 октября 2011

Для полноты картины, если вам когда-либо понадобится пойти другим путем, читая свойства в стиле javabean из классов scala, вы можете использовать @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());
...