简体   繁体   English

Scala:如何通过属性丰富课程

[英]Scala: How to enrich a class with a property

I'm trying to enrich a class with a custom property: 我正在尝试通过自定义属性丰富类:

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)

Then I try to access custom property name like this... 然后,我尝试像这样访问自定义属性name ...

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

... but it does not compile: ...但是它不能编译:

value printNumber is not a member of MyClassWithName.

Am I missing something? 我想念什么吗? Tx. Tx。

You probably wanted the opposite: 您可能想要相反的情况:

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

Note, that newer versions of scala has more concise syntax with implicit class combination 注意,新版本的scala具有implicit class组合的更简洁的语法

If you only want to add a name property and nothing else you could do it like this: 如果您只想添加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
  }
}

You can then access name as if it's a member of MyClass : 然后,您可以像访问MyClass一样访问name:

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

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

scala> c.name
res4: String = foo

Note that with this approach the equals method of MyClass should test for reference equality. 请注意,使用这种方法, MyClassequals方法应该测试引用是否相等。 Otherwise the Map has to be changed to use reference equality instead. 否则,必须将Map更改为使用引用相等。


If you want to be able to add many properties to an object at runtime and you can change the definition of the class you can extend Dynamic . 如果您希望能够在运行时向对象添加许多属性,并且可以更改类的定义,则可以扩展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)
}

Then you can add and access properties like this: 然后,您可以添加和访问如下属性:

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

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

scala> c.name
res6: String = foo

If you can't change the definition of the class, you could try to do some sort of implicit conversion like this: 如果您不能更改类的定义,则可以尝试执行某种隐式转换,如下所示:

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))
}

Then you can add and access properties, but with a less intuitive syntax: 然后,您可以添加和访问属性,但语法不太直观:

scala> val c1 = new MyClass
c1: MyClass = MyClass@635b4736

scala> val c2 = new MyClass
c2: MyClass = MyClass@1be2e9e5

scala> c1("name") = "foo"

scala> c2("name") = "bar"

scala> c1("name")
res2: String = foo

scala> c2("name")
res3: String = bar

Your wrapper class MyClassWithName contains myClass and name as its members. 包装器类MyClassWithName包含myClass和其成员name So, in order to access the printNumber function in the class MyClass , you need to access the object myClass first. 因此,为了访问MyClass类中的printNumber函数,您需要首先访问对象myClass

You can write a getter method, use @BeanProperty annotation or better yet, use a case class as follows: 您可以编写一个getter方法,使用@BeanProperty注解或更好的方法,使用一个case类,如下所示:

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

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

That would compile. 那会编译。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM