4

How can I get all of the objects within an object with reflection?

Consider this code:

object MonthDay extends MyEnum {
  //Some important holidays
  object NewYear       extends MonthDay( 1,  1)
  object UnityDay      extends MonthDay(11,  9)
  object SaintNicholas extends MonthDay(12,  6)
  object Christmas     extends MonthDay(12, 24)
}

class MonthDay(month: Int, day: Int)

trait MyEnum {
  val values: List[MonthDay] = this.getClass.getField("MODULE$")...
  val next: MonthDay = ...
  val previous: MonthDay = ...
}

//Of course the user can create his own MonthDays
val myBirthDay = new MonthDay(month, day)

if(!MonthDay.values.contains(myBirthDay)) "Well, I probably have to work"
else "Great, it is a holiday!"

I want to have a trait (MyEnum) which I can mix into the object holding my "enumeration objects" with methods to return a list of them (def values: List[MonthDay]) or iterate over them (def next: MonthDay or def previous: MonthDay) without repeating myself a few times (this is absolutely crucial!).

The idea is that values accesses the MonthDay object and finds all singleton objects of the class they are extending (MonthDay) with reflection.

Suma
  • 33,181
  • 16
  • 123
  • 191
soc
  • 27,983
  • 20
  • 111
  • 215

3 Answers3

1

Something similar is done in Enumeration.populateNameMap: https://lampsvn.epfl.ch/trac/scala/browser/scala/tags/R_2_8_1_final/src/library/scala/Enumeration.scala

Landei
  • 54,104
  • 13
  • 100
  • 195
  • I already tried to do that, but I can't see the objects/fields/methods if I look into MonthDay.MODULE$. – soc Nov 21 '10 at 21:19
  • @soc: he was referring to the line `val methods = getClass.getMethods filter (m => m.getParameterTypes.isEmpty && classOf[Value].isAssignableFrom(m.getReturnType))` which uses plain-old Java reflection to find parameterless functions that return the right type, and assumes that every one of those is an Enumeration value. I'd imagine that one day when we have real *Scala* reflection, we'll be able to tell whether these methods were created with `object`, `val` or `def` (to exclude the `def`s). (And it appears this method should work even if you declare your objects as `object`) – Ken Bloom Nov 23 '10 at 00:25
  • He's also referring to the m.invoke() just a little further down that turns `methods` into `values`. – Ken Bloom Nov 23 '10 at 00:48
  • @Ken Bloom: Yes I understood that. The problem is that reflection currrently doesn't work as it should in my case. :-( – soc Nov 23 '10 at 17:26
1

My solution, based on Landei's answer would be:

trait MyEnum{
   def valsOfType[T:Manifest] = {
      val c=implicitly[Manifest[T]].erasure
      for {m <- getClass.getMethods 
           if m.getParameterTypes.isEmpty && c.isAssignableFrom(m.getReturnType)
      } yield (m.invoke(this).asInstanceOf[T])
   }
}

class MonthDay(month:Int,day:Int)

object MonthDay extends MyEnum {
   //maybe you want to call this "holidays" instead
   lazy val values = valsOfType[MonthDay] 

   val NewYear       = new MonthDay( 1,  1)
   val UnityDay      = new MonthDay(11,  9)
   val SaintNicholas = new MonthDay(12,  6)
   val Christmas     = new MonthDay(12, 24)
}

I don't think you should call this MyEnum anymore, because an enumerated type implies a closed set of values.

(Doesn't work if the enumeration values are defined as objects)

Community
  • 1
  • 1
Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
  • That looks really interesting! This might be an approach worth using until the real solution works (`getClass.getDeclaredClasses`, currently not working, see this bug report: http://lampsvn.epfl.ch/trac/scala/ticket/4023) – soc Nov 23 '10 at 01:24
  • Sure, I'm open to adopt a better name for it. Do you have an idea? – soc Nov 23 '10 at 01:25
  • @soc: maybe `FindContainedVals` – Ken Bloom Nov 23 '10 at 01:27
  • Mhh, while it describes the technology behind it, it doesn't really describe my intent ... *Basically I want concise DRY code, the possibility to add my own methods, having the possibility for the user to instantiate their own instances without a type mismatch with the declared instances and need "self-awareness" so that the object knows how many objects it actually holds (and can retrieve them). Everything without inventing special syntaxes or rules like registering the instances.* That's how I characterized it in another comment. – soc Nov 23 '10 at 13:56
  • Maybe: `trait FlexibleEnumeratedSubsetOfClassInstancesWithUsefulConvenienceMethods`? :-) – soc Nov 23 '10 at 13:58
0

You should be able to use the pre-existing Scala Enumeration class: http://www.scala-lang.org/api/current/scala/Enumeration.html

It seems to come very close to your use-case!

Kevin Wright
  • 49,540
  • 9
  • 105
  • 155