Scala: set a field value reflectively from field name - reflection

I'm learning scala and can't find out how to do this:
I'm doing a mapper between scala objects and google appengine entities, so if i have a class like this:
class Student {
var id:Long
var name:String
}
I need to create an instance of that class, in java i would get the Field by it's name and then do field.set(object, value) but I can't find how to do so in scala.
I can't use java reflection since the fields of Student are seen as private and field.set throws an error because of that.
Thanks

Scala turns "var" into a private field, one getter and one setter. So in order to get/set a var by identifying it using a String, you need to use Java reflection to find the getter/setter methods. Below is a code snippet that does that. Please note this code runs under Scala 2.8.0 and handlings of duplicated method names and errors are nonexistent.
class Student {
var id: Long = _
var name: String = _
}
implicit def reflector(ref: AnyRef) = new {
def getV(name: String): Any = ref.getClass.getMethods.find(_.getName == name).get.invoke(ref)
def setV(name: String, value: Any): Unit = ref.getClass.getMethods.find(_.getName == name + "_$eq").get.invoke(ref, value.asInstanceOf[AnyRef])
}
val s = new Student
s.setV("name", "Walter")
println(s.getV("name")) // prints "Walter"
s.setV("id", 1234)
println(s.getV("id")) // prints "1234"

Related

Failure to discover property with reflection

Taking the following minimal example:
type IMyInterface =
interface
abstract member Name: string with get
end
let testInstance =
{ new IMyInterface with
member _.Name = "Hello Word" }
I would have naively expected a call to testInstance.GetType().GetProperties() to contain a PropertyInfo element corresponding to Name.
However, only an empty array is returned.
Using testInstance.GetType().GetProperty("Name") yields no better as it simply returns a <null> object.
More confusing still, Visual Studio 2022 IntelliSense lists Name as a valid property (as I'd expect).
How can I get a PropertyInfo corresponding to the Name property?
In F# all interface implementations are private. This means that interface methods and properties do not appear as methods and properties of the implementing class.
In C# this works a bit differently: if you define a public member that happens to match an interface member, you don't have to explicitly tell the compiler that it's meant to be the interface implementation, the compiler will map it to the interface automatically for you.
So, for example, if you write this:
class MyClass : IMyInterface {
public string Name { get; }
}
The C# compiler will actually compile it as this:
class MyClass : IMyInterface {
public string Name { get; }
string IMyInterface.Name { get { return this.Name; } }
}
(well, it's not exactly like that, but you get the idea)
But the F# compiler doesn't do that. If you want a class property in addition to the interface property, you have to roll one yourself:
type MyClass() =
member __.Name = "Hello Word"
interface IMyInterface with
member this.Name = this.Name
But if you just want the interface property, you can get it off of the interface type:
let nameProp = typeof<IMyInterface>.GetProperty("Name")
let helloWorld = nameProp.GetValue testInstance
Or, if you don't know the interface type in advance, you can get it from the object type as well:
let intf = testInstance.GetType().GetInterfaces().[0]
let nameProp = intf.GetProperty("Name")
let helloWorld = nameProp.GetValue testInstance

FlowJs - class is incompatible with interface it implements while used in array

I come from C# background and trying to do the thing that would work there, but for some reason does not work with flow js.
I have interface and a class that implements interface:
interface Id {
id: number
}
class A implements Id {
id: number
}
When I try to use object of a class in place of interface this way, everything works:
var a = new A()
function f(v: Id) {}
f(a)
But when I try to use array of objects, it gives me an error:
var array: Array<A> = [ new A(), new A() ]
function f(array: Array<Id>){}
f(array)
Error is:
'Id (This type is incompatible with A)'
Why is that?

Realm Swift updating objects not working

I'm using realm swift v1.0.0 and I create an object and I want to update its value permanently.
Following the official guide and some other answers on stackoverflow I've done:
let myobject = MyObjectClass()
myobject.property1 = "test"
try! myrealm.write {
myrealm.add(myobject)
}
And myrealm is a global variable:
let myrealm = try! Realm()
When I recover all the values of MyObjectClass I get back myobject with some initial values set in the init of the class (so the values that I set as initial values are stored properly), but property1 is (null) (as it is declared as an optional string).
But if I look at myobject in the function where I write the code above, its property1 is set to "test", but it's not saved permanently in the Realm db.
Can anyone help me to find out why it doesn't update the value of property1?
I've also tried to do:
try! myrealm.write {
myrealm.add(myobject, update: true)
}
EDIT:
This is my model:
public class MyObjectClass(): Object {
//[...]
public dynamic var property1: String? = nil
public dynamic var property2: MyObjectClass2?
}
Same problem happens with property2 which is another Realm Object.

Instantiate Xbase XExpressions programmatically

I'm working on an Xtext project that uses parts of the Xbase grammar. In my code I'm analysing the DSL program in order to generate some Java code. It's easy to turn an existing XExpression into Java code using XbaseCompiler but that's not what I need. Instead, I need to generate a different program.
Currently I'm simply generating Java code (i.e. constructing a String). What I'd like to do is constructing a new XExpression, maybe even using parts of the original one, and then compile it to Java.
The different XExpressions have zero-args constructors, so I tried instantiating one and even set some of the properties. However the compiler crashed and I can't work out which properties it is missing.
I would prefer to generate the expression as String and then use the Xtext parser to do the heavy lifting for you, i.e. let it parse the String and create the corresponding XExpression objects for you. How you do that properly depends on the context you're in (OSGI or standalone).
Here is a standalone example (can be run from a simple main method) for the "Scripting Language" from the Xtext documentation:
public class StandaloneParser {
#Inject
private IParser parser;
public StandaloneParser() {
Injector injector = new ScriptingStandaloneSetup().createInjectorAndDoEMFRegistration();
injector.injectMembers(this);
}
public EObject parse(String input) {
IParseResult result = parser.parse(new StringReader(input));
if (result.hasSyntaxErrors()) {
throw new RuntimeException("Syntax errors");
}
return result.getRootASTElement();
}
}
Example for a caller:
public class Main {
public static void main(String[] args) {
StandaloneParser parser = new StandaloneParser();
EObject result = parser.parse("val answer = 7 * 6;");
System.out.println(result);
}
}
If you try to create such an expression programatically you'll have a hard time. It could look like this (Xtend code):
val factory = XbaseFactory.eINSTANCE
val expression = factory.createXVariableDeclaration => [
name = "answer"
right = factory.createXBinaryOperation => [
leftOperand = factory.createXNumberLiteral => [
value = "7"
]
feature = // TODO retrieve the JvmOperation: org.eclipse.xtext.xbase.lib.IntegerExtensions.operator_multiply(int,int)
rightOperand = factory.createXNumberLiteral => [
value = "6"
]
]
]

Passing case class between Scala and Flex using BlazeDS

When trying to pass a "case class" from Scala to Flex the results on Flex side are regular Object instead of UINamespace. Changing the case class to regular Java class and it arrives correctly.
The case class looks like this:
package com.scala.vo
case class UINamespace (#BeanProperty var name : String,
#BeanProperty var version : String,
#BeanProperty var parameters : java.util.List[String]) {
def this() = this("", "", null)
}
Flex Side:
[RemoteClass(alias="com.scala.vo.UINamespace")]
public class UINamespace
{
public var name : String;
public var version : String;
public var parameters : ArrayCollection;
}
Anyone managed to solve this?
[Edited] Changed null to regular object result.
I tried it out again today and it worked. I am not sure what was the exact cause for it not to work when asking the question.
I'll leave the question here so if others have issues with case class, at least they will know that it should be working.

Resources