Scala中的动态属性
Scala支持类似动态属性的东西吗?例如: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”这样允许像普通字段一样使用添加属性的东西,那就足够了。
我对结构类型声明(“类型安全鸭子打字”)不感兴趣。我真正需要的是在运行时添加新的属性和方法,以便该对象可以被期望添加的元素存在的方法/代码使用。
斯卡拉2.9将有一个特殊处理的动态特质,可能是你在找什么。
这个博客有一个大一下:http://squirrelsewer.blogspot.com/2011/02/scalas-upcoming-dynamic-capabilities.html
我猜想,在invokeDynamic方法,你将需要检查“名_ =”,“speak_ =”,“名”和“说话”,并您可以将值存储在私人地图中。
Scala 2.9的动态支持仍然非常不完整。它不适用于作业或应用(),请参阅这里[链接](http://groups.google.com/group/scala-debate/browse_thread/thread/77bc9e4edcdf5628) – ACyclic 2011-06-18 20:16:41
我想不出什么理由来真的需要添加/在运行时动态创建方法/属性除非动态标识也是允许的 - 和/或一个神奇结合的外部动力源(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)实现中,排序图可能是更好/可行的方法。
快乐编码。
首先,正如@pst指出的那样,您的示例可以完全使用静态类型实现,而不需要动态类型。其次,如果你想用动态类型语言编程,用动态类型语言编程。
这就是说,你可以实际上在斯卡拉做类似的事情。这里是一个简单的例子:
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
是一个特性,而不是一个类,所以你不能实例化它,你必须混合它。而且你显然必须实际定义你想要它做什么,即实现typed
和applyDynamic
。
如果你想要你的例子工作,有一些并发症。特别是,您需要类似于类型安全的异构映射作为后备存储。另外,还有一些语法考虑。例如,如果foo.bar_=
存在,则foo.bar = baz
仅被翻译为foo.bar_=(baz)
,它不存在,因为foo
是Dynamic
对象。
这将工作在Scala 2.8.1上吗? – fernacolo 2011-03-20 12:52:35
@fernacolo:不,“Dynamic”是2.9中的新功能。 – 2011-03-20 13:39:30
我认为“黄玉告诉无效”是一个很好的迹象,为什么应该尽可能避免这种事情。呃,我的意思是,woof。 – 2011-03-20 03:49:12
我同意。这只是一个例子。在现实世界中,我将使用它从JSON或XML读取域对象,并且如果属性不存在,我不希望它失败,也不想编码一些if或条件代码。下次我会尝试一个更好的例子。 – fernacolo 2011-03-20 13:56:04