Scala是否支持动态属性?例如:
val dog = new Dynamic // Dynamic does not define 'name' nor 'speak'.
dog.name = "Rex" // New property.
dog.speak = { "woof" } // New method.
val cat = new Dynamic
cat.name = "Fluffy"
cat.speak = { "meow" }
val rock = new Dynamic
rock.name = "Topaz"
// rock doesn't speak.
def test(val animal: Any) = {
animal.name + " is telling " + animal.speak()
}
test(dog) // "Rex is telling woof"
test(cat) // "Fluffy is telling meow"
test(rock) // "Topaz is telling null"
我们可以在Scala中获得的最接近的东西是什么?如果有类似“addProperty”的东西允许像普通字段一样使用添加的属性,那就足够了。
我对结构类型声明(“type safe duck typing”)不感兴趣。我真正需要的是在运行时添加新的属性和方法,以便该对象可以被期望添加的元素存在的方法/代码使用。
答案 0 :(得分:8)
Scala 2.9将具有特殊处理的动态特性,可能正是您所寻找的。 p>
这个博客有很大的关注:http://squirrelsewer.blogspot.com/2011/02/scalas-upcoming-dynamic-capabilities.html
我猜想在invokeDynamic方法中你需要检查“name_ =”,“speak_ =”,“name”和“speak”,你可以将值存储在私有地图中。
答案 1 :(得分:3)
我想不出真的需要在运行时动态添加/创建方法/属性的原因,除非动态标识符也被允许 - 和/或 - 与外部动态源的神奇绑定(JRuby或JSON是两个很好的例子)。
否则,发布的示例可以完全使用Scala中现有的静态类型通过“匿名”类型和结构类型实现。无论如何,并不是说“动态”不方便(并且正如0__指出的那样,即将到来 - 随意“走向边缘”; - )。
考虑:
val dog = new {
val name = "Rex"
def speak = { "woof" }
}
val cat = new {
val name = "Fluffy"
def speak = { "meow" }
}
// Rock not shown here -- because it doesn't speak it won't compile
// with the following unless it stubs in. In both cases it's an error:
// the issue is when/where the error occurs.
def test(animal: { val name: String; def speak: String }) = {
animal.name + " is telling " + animal.speak
}
// However, we can take in the more general type { val name: String } and try to
// invoke the possibly non-existent property, albeit in a hackish sort of way.
// Unfortunately pattern matching does not work with structural types AFAIK :(
val rock = new {
val name = "Topaz"
}
def test2(animal: { val name: String }) = {
animal.name + " is telling " + (try {
animal.asInstanceOf[{ def speak: String }).speak
} catch { case _ => "{very silently}" })
}
test(dog)
test(cat)
// test(rock) -- no! will not compile (a good thing)
test2(dog)
test2(cat)
test2(rock)
但是,这种方法很快就会变得很麻烦(“添加”创建新类型所需的新属性并将当前数据复制到其中)并且部分利用了示例代码的简单性。也就是说,以这种方式创建真正的“开放”对象实际上是不可能的;对于“开放”数据,在当前的Scala(2.8)实现中,各种Map可能是更好/可行的方法。
快乐的编码。
答案 2 :(得分:2)
首先,正如@pst指出的那样,您的示例可以使用静态类型完全实现,它不需要动态输入。
其次,如果您想用动态类型语言编程,请使用动态类型语言进行编程。
话虽这么说,你可以在Scala中实际执行类似的操作。这是一个简单的例子:
class Dict[V](args: (String, V)*) extends Dynamic {
import scala.collection.mutable.Map
private val backingStore = Map[String, V](args:_*)
def typed[T] = throw new UnsupportedOperationException()
def applyDynamic(name: String)(args: Any*) = {
val k = if (name.endsWith("_=")) name.dropRight(2) else name
if (name.endsWith("_=")) backingStore(k) = args.first.asInstanceOf[V]
backingStore.get(k)
}
override def toString() = "Dict(" + backingStore.mkString(", ") + ")"
}
object Dict {
def apply[V](args: (String, V)*) = new Dict(args:_*)
}
val t1 = Dict[Any]()
t1.bar_=("quux")
val t2 = new Dict("foo" -> "bar", "baz" -> "quux")
val t3 = Dict("foo" -> "bar", "baz" -> "quux")
t1.bar // => Some(quux)
t2.baz // => Some(quux)
t3.baz // => Some(quux)
正如你所看到的,实际上你非常接近。你的主要错误是Dynamic
是一个特征,而不是一个类,所以你不能实例化它,你必须混合它。你显然必须实际定义你想要它做什么,即实现{ {1}}和typed
。
如果您希望 示例正常工作,则会出现一些并发症。特别是,您需要类似安全的异类映射作为后备存储。此外,还有一些语法上的考虑因素。例如,如果applyDynamic
存在foo.bar = baz
,foo.bar_=(baz)
只会被转换为foo.bar_=
,而foo
只是Dynamic
对象。