Scala:如何用属性来丰富一个类

时间:2014-06-20 18:44:03

标签: scala

我试图用自定义属性来丰富一个类:

class MyClass {

   def printNumber(n: Int) = {
     println(n)
   }
}

class MyClassWithName(myClass: MyClass, val name: String) {}

implicit def myClassWithName(myClass: MyClass, name: String) = new MyClassWithName(myClass, name)

然后我尝试像这样访问自定义属性name ...

val c = myClassWithName(new MyClass, "jonny")
c.printNumber(5)

...但它没有编译:

value printNumber is not a member of MyClassWithName.

我错过了什么吗? TX。

3 个答案:

答案 0 :(得分:2)

你可能想要相反:

class Wrapper(original: OriginalClass) {
  def printNumber(n: Int) = println(n)
  def printName = println(original.name) // I added this method to justify wrapping of original
}

class OriginalClass(val name: String)

implicit def toWrapper(original: OriginalClass) = new Wrapper(original)

val x = new OriginalClass("johny")
// x: OriginalClass = OriginalClass@21788153

x.printName
// johny

x.printNumber(1)
// 1

请注意,较新版本的scala具有更简洁的implicit class combination

语法

答案 1 :(得分:2)

如果您只想添加name属性而无需其他任何内容,您可以这样做:

object MyClassPlus {
  private val map = scala.collection.mutable.Map.empty[MyClass,String]
}

implicit class MyClassPlus(val myClass: MyClass) extends AnyVal {

  def name = MyClassPlus.map(myClass) 

  def name_= (value: String) {
    MyClassPlus.map(myClass) = value
  }
}

然后,您可以访问名称,就像它是MyClass的成员一样:

scala> val c = new MyClass
c: MyClass = MyClass@78e312af

scala> c.name = "foo"
c.name: String = foo

scala> c.name
res4: String = foo

请注意,使用此方法时,equals的{​​{1}}方法应测试引用相等性。否则,必须将MyClass更改为使用引用相等。


如果您希望能够在运行时向对象添加许多属性,并且可以更改类的定义,则可以扩展Map

Dynamic

然后你可以添加和访问这样的属性:

import scala.language.dynamics

class MyClass extends Dynamic {

  private val map = scala.collection.mutable.Map.empty[String,String]

  // the type of value doesn't necessarily have to be String
  def updateDynamic(name: String)(value: String) {
    map(name) = value
  }

  def selectDynamic(name: String) = map(name)
}

如果无法更改类的定义,可以尝试进行某种类型的隐式转换:

scala> val c = new MyClass
c: MyClass = MyClass@50f85a5f

scala> c.name = "foo"
c.name: String = foo

scala> c.name
res6: String = foo

然后您可以添加和访问属性,但语法不太直观:

object MyClassPlus {
  private val map = scala.collection.mutable.Map.empty[(MyClass,String),String]
}

implicit class MyClassPlus(val myClass: MyClass) extends AnyVal {

  def update(name: String, value: String) {
    MyClassPlus.map((myClass, name)) = value
  }

  def apply(name: String) = MyClassPlus.map((myClass, name))
}

答案 2 :(得分:1)

您的包装类MyClassWithName包含myClassname作为其成员。因此,要访问类printNumber中的MyClass函数,您需要先访问对象myClass

您可以编写一个getter方法,使用@BeanProperty注释或者更好,使用案例类如下:

case class MyClassWithName(myClass: MyClass, val name: String)

val c = myClassWithName(new MyClass, "jonny")
c.myClass.printNumber(5)

那会编译。