2011-03-20 120 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