假设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可以这样吗?
答案 0 :(得分:6)
您可以导入设置者,因此您无需限定呼叫:
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)
)
答案 1 :(得分:6)
您可以将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 :(
答案 2 :(得分:1)
为了完整起见,如果你需要反过来,从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());
答案 3 :(得分:1)
我会写一个隐式转换来使用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")
}