2011-03-20 121 views
1

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”这样允许像普通字段一样使用添加属性的东西,那就足够了。

我对结构类型声明(“类型安全鸭子打字”)不感兴趣。我真正需要的是在运行时添加新的属性和方法,以便该对象可以被期望添加的元素存在的方法/代码使用。

+5

我认为“黄玉告诉无效”是一个很好的迹象,为什么应该尽可能避免这种事情。呃,我的意思是,woof。 – 2011-03-20 03:49:12

+0

我同意。这只是一个例子。在现实世界中,我将使用它从JSON或XML读取域对象,并且如果属性不存在,我不希望它失败,也不想编码一些if或条件代码。下次我会尝试一个更好的例子。 – fernacolo 2011-03-20 13:56:04

回答

7

斯卡拉2.9将有一个特殊处理的动态特质,可能是你在找什么。

这个博客有一个大一下:http://squirrelsewer.blogspot.com/2011/02/scalas-upcoming-dynamic-capabilities.html

我猜想,在invokeDynamic方法,你将需要检查“名_ =”,“speak_ =”,“名”和“说话”,并您可以将值存储在私人地图中。

+0

Scala 2.9的动态支持仍然非常不完整。它不适用于作业或应用(),请参阅这里[链接](http://groups.google.com/group/scala-debate/browse_thread/thread/77bc9e4edcdf5628) – ACyclic 2011-06-18 20:16:41

2

我想不出什么理由来真的需要添加/在运行时动态创建方法/属性除非动态标识也是允许的 - 和/或一个神奇结合的外部动力源(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)实现中,排序图可能是更好/可行的方法。

快乐编码。

1

首先,正如@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是一个特性,而不是一个类,所以你不能实例化它,你必须混合它。而且你显然必须实际定义你想要它做什么,即实现typedapplyDynamic

如果你想要你的例子工作,有一些并发症。特别是,您需要类似于类型安全的异构映射作为后备存储。另外,还有一些语法考虑。例如,如果foo.bar_=存在,则foo.bar = baz仅被翻译为foo.bar_=(baz),它不存在,因为fooDynamic对象。

+0

这将工作在Scala 2.8.1上吗? – fernacolo 2011-03-20 12:52:35

+2

@fernacolo:不,“Dynamic”是2.9中的新功能。 – 2011-03-20 13:39:30