enumerate class variables

mterveen

Member
Licensed User
Longtime User
i am using a class with a number of "properties" using the set/get feature. i want to be able to instantiate a class and then enumerate the variables and variable types that make up the class. For example:


myClass:
B4X:
Sub Class_Globals
   private v_i as int
End sub

Sub seti(e as int)
   v_i = e
End sub
Sub geti as int
   return v_i
end sub

B4X:
Sub Enumerate
   dim c as myClass
   
'this is what i want to do next
   for each variable in c
       log(variable.name & ":" & variable.type)     'or something similar
   next

end sub

Possible? can it be done using reflection? i really like using classes but i suppose i could move it into a map or keyvaluestore in needed.

**edit**
basically something like listing 3 in this link http://www.ibm.com/developerworks/library/j-dyn0603/
 
Last edited:

stevel05

Expert
Licensed User
Longtime User
I hadn't seen this post when I answered the one in the reflection thread, there is an answer there to that question, but also if you do:

B4X:
Log(c)
you will get a toString representation of the class which lists the global variables.

You can't assign it to a string directly, but you can use reflection to get the toString for the class.

As a sub in the class:

B4X:
Sub ToString As String
   Dim R As Reflector
   R.Target=Me
   Return R.RunMethod("toString")
End Sub

Then you can slice that as you wish.

Or you can get a list of the variables relevant to the class through reflection as:

B4X:
Dim c As myclass
Dim r As Reflector
r.Target=c
r.Target=r.RunMethod("getClass")
Dim Fields() As Object = r.RunMethod("getDeclaredFields") 'or one field as r.Target= r.RunMethod2("getDeclaredField","_v_i","java.lang.String")
For Each Field As Object In Fields 
   r.Target=Field
   Log(Field&" "&r.RunMethod("getType"))
Next

You'll need to filter out those you don't want or you can get one field at a time by name (see comments).

You'll need the reference for Field, and maybe class if you want to follow it through and make changes.
 
Last edited:
Upvote 0

mterveen

Member
Licensed User
Longtime User
reflection

thanks a billion steve. that is EXACTLY what i needed. i saw the "getDeclaredFields" java stuff but couldn't figure out the format to use it in the reflection library. thought that maybe it needed to be a specific member of the library. i love the reflection library and your post clarified a number of things for me.
 
Upvote 0

mterveen

Member
Licensed User
Longtime User
value of field

steve - one more question.

here is my test code:

B4X:
   Dim c As myClass
   Dim r As Reflector
   r.Target=c
   r.Target=r.RunMethod("getClass")
   Dim Fields() As Object = r.RunMethod("getDeclaredFields") 'or one field as r.Target= r.RunMethod2("getDeclaredField","_v_i","java.lang.String")
   For Each Field As Object In Fields 
      r.Target=Field
      Dim ftype As String = r.RunMethod("getType")
      If ftype.ToLowerCase = "int" Then
         Log("type:" & ftype)
         Log("name:" & r.RunMethod("getName"))
         Log("gentype:" & r.RunMethod("getGenericType"))
         Log("value:" & r.RunMethod2("getInt","_v_i","int"))  'crashes
         Log("field:" & Field)
      End If
   Next

can't figure out the format for the "get value" side. i know that

B4X:
r.target = c
log(r.getfield("_v_i")

works. however, i want to be able to get the value in the loop versus declaring each variable in "getfield". the link shows various methods to get values including

get(Object object)
Returns the value of the field in the specified object.

and

getInt(Object object)
Returns the value of the field in the specified object as an int.

? is how do you define the object?
 
Upvote 0

mterveen

Member
Licensed User
Longtime User
value of field

ok - got this to work in the loop to return the value field:

before the loop:

B4X:
Dim args(1) As Object
Dim types(1) As String
args(0) = c ' the object whose field to access
types(0) = "java.lang.Object" ' get method parameter type

i place this in the loop:

B4X:
Log("value:" & r.RunMethod4("get", args, types))

i guess i'm happy with this although i don't quite understand it!! i don't see why the type has to be "java.lang.Object" and why "java.lang.Int" doesn't work, especially since my loop is only looking at integer types.
 
Upvote 0

agraham

Expert
Licensed User
Longtime User
i don't see why the type has to be "java.lang.Object"
Because the declared signature of "get" is
B4X:
public Object get(Object object)
and to find the method to run you need to use the declared signature of that method in order for it to be located. There is no way to know at runtime, except by fetching it and querying its type, that the object is actually an int as it could be anything.
 
Upvote 0

mterveen

Member
Licensed User
Longtime User
how do i run the specific getter/setter subs? in particular using the info in post 1, i can easily retrieve and modify the variable v_i using the info from the other posts. however, since my getter/setter subs have additional logic in them i would prefer to run those subs vs modifying the variable directly.

possible, and if so, how?
 
Upvote 0

mterveen

Member
Licensed User
Longtime User
after half a day of messing around figured it out. basically,

myclass:
B4X:
'Class module
Sub Class_Globals
    Private v_i As Int = 10
End Sub

'Initializes the object. You can add parameters to this method if needed.
Public Sub Initialize
End Sub
Sub seti(e As Int)
  v_i = e
End Sub
Sub geti As Int
  'Return v_i
  Return 30
End Sub

main activity
B4X:
Sub Activity_Create(FirstTime As Boolean)
   'Do not forget to load the layout file created with the visual designer. For example:
   'Activity.LoadLayout("Layout1")
   Enumerate
End Sub

Sub Enumerate
Dim c As myclass
c.Initialize
Dim r1, r2 As Reflector
r2.Target=c
r1.Target=r2.RunMethod("getClass")
Dim Fields() As Object = r1.RunMethod("getDeclaredFields") 
For Each Field As Object In Fields
      r1.Target=Field
      Dim vname As String = r1.RunMethod("getName")
      If vname.StartsWith("_v_") Then
      Log(vname)
      Log(r2.GetField(vname))
      Dim getter As String = vname.Replace("_v_","_get")
          Log(r2.RunMethod(getter))
    End If
Next

results are:
_v_i
10
30

perfect! whew!
 
Upvote 0
Top