Consider the following code:
class Foo(var name: String = "bar")
Now i try to get the value and the correct type of it via reflection:
val foo = new Foo
val field = foo.getClass.getDeclaredField("name")
field.setAccessible(true)
//This is where it doesn't work
val value = field.get(????)
I tried things like field.get(foo), but that just returns an java.lang.Object but no String. Basically I need the correct type, because I want to invoke a method on it (e. g. toCharArray).
What is the suggested way to do that?
As others have mentioned, the reflection methods return Object so you have to cast. You may be better using the method that the Scala compiler creates for field access rather than having to change the visibility of the private field. (I'm not even sure if the name private field is guaranteed to be the same as that of the accessor methods.)
val foo = new Foo
val method = foo.getClass.getDeclaredMethod("name")
val value = method.get(foo).asInstanceOf[String]
getDeclaredField is a method of java.lang.Class.
You have to change foo.getDeclaredField("name") to foo.getClass.getDeclaredField("name") (or classOf[Foo].getDeclaredField("name")) to get the field.
You can get the type with getType method in class Field but it won't help you because it returns Class[_]. Given than you know that the type is a String you can always cast the value returned using field.get(foo).asInstanceOf[String]
AFAIK, reflection always work with Object, and you have to cast the results yourself.
This is how one can get list of fieldnames and its value of a case class:
First, using reflection, get fields info as follows -
val TUPLE2_OF_FIELDNAME_TO_GETTERS = typeOf[<CLASS>].members
.filter(!_.isMethod)
.map(x => (x.name.toString, classOf[<CLASS>].getDeclaredMethod(x.name.toString.trim)))
How to use it?
getFieldNameAndValue(obj: <CLASS>): Seq[(String, String)] {
var output = Seq[(String, String)]()
for(fieldToGetter <- TUPLE2_OF_FIELDNAME_TO_GETTERS) {
val fieldNameAsString = fieldToGetter._1
val getter = fieldToGetter._2
val fieldValue = getter.invoke(obj).toString
output += (fieldName, fieldValue)
}
}
foo.getClass.getDeclaredField("name").getString(foo)
should work if you want to avoid asInstanceOf. get* is available for various types
Related
I have code accepts a class as a parameter and prepares data to call either the constructor for that class of a companion object factory method if the factory method is present.
All works fine when calling the constructor, but I get the error
java.lang.IllegalArgumentException: No argument provided for a required parameter: instance of fun nz.salect.objjson.JVMTest.StudentWithFactory.Companion.fromJson(kotlin.String, kotlin.Int): nz.salect.objjson.JVMTest.StudentWithFactory
when calling the factory method. The factory method in question:
data class StudentWithFactory(val name: String, val years: Int=0) {
companion object {
fun fromJson(name: String="", age: Int = 0):StudentWithFactory {
return StudentWithFactory(name, age)
}
}
}
has no required parameters, unless there is some hidden parameter. Any ideas?
In fact, I reverted removing the parameters completely from fromJson and directly calling the companion method using ::fromJson.callby(emptyMap()). Same error.
It is clear that companion methods need at least one additional parameter. Perhaps the class? Or the companion object?
How can I specify the needed parameter(s)?
The function building up the callBy() is supplied a class (or finds the class from a supplied class) and json names and values.
var funk:KFunction<*>?=null
val companionFuncs=cls.companionObject?.declaredMemberFunctions
if(companionFuncs?.size ?:0 >0){
companionFuncs?.forEach {
if(it.name == "fromJson") funk=it
}
}
val cons:KFunction<T> = if(funk != null)
funk as KFunction<T>
else
cls.primaryConstructor ?: throw IllegalArgumentException("no primary constructor ${cls.simpleName}")
val valuesMap = cons.parameters.filter{it.name in vals}
.associateBy(
{it},
{generateValue(it)}
)
val data = cons.callBy(valuesMap) //as T
return data
In addition to my short answer, a more technical explanation:
Yes, there actually is a hidden parameter and you can see it (for example), if you take a look at the decompiled (to Java) bytecode:
public final class StudentWithFactory {
// ...
public static final class Companion {
// ...
#NotNull
public static StudentWithFactory fromJson$default(StudentWithFactory.Companion var0, String var1, int var2, int var3, Object var4) {
// ...
return var0.fromJson(var1, var2);
}
// ...
}
}
The first parameter (var0) is actually an instance of the companion object. var1 (name) and var2 (age) are the parameters you declared. var3 is a bitmask for determining if explicit values have been passed or if the default ones should be used*. I honestly don't know what var4 is for. It is unused in the Java code. But the imported part is that you only need to worry about var0, var1 and var2 if you want to invoke the function.
So, in the end the non-static version of fromJson* is actually invoked on the instance of the companion object:
var0.fromJson(var1, var2)
*left code out for simplicity
You can use the parameters property to determine how much parameters you have to pass to the function/constructor.
If you call
val paramsConstr = StudentWithFactory::class.primaryConstructor?.parameters
paramsConstr will be of size two as expected, but if you call
val paramsFunc = ::fromJson.parameters
paramsFunc will be of size three. The first element corresponds to the instance of the companion object. So, thats the list of parameters you need to provide.
You can invoke the fromJson like this:
// not using any default parameters
::fromJson.callBy(mapOf(
paramsFunc[0] to StudentWithFactory::class.companionObjectInstance,
paramsFunc[1] to "Hello",
paramsFunc[2] to 30
))
// using only the default parameter for "name"
::fromJson.callBy(mapOf(
paramsFunc[0] to StudentWithFactory::class.companionObjectInstance,
paramsFunc[2] to 30
))
I've got a method that does the following:
val type = AClass::class.java.methods[0].parameters[0].type
val toDeserialise = SecondClass<type>()
My SecondClass is:
class SecondClass<T : Any> {
lateinit var p1: T
}
But this doesn't work. Basically, I want to take a type of a method's parameter and pass it into a SecondClass. Do you know how to implement this?
The AClass is:
class AClass{
fun myMethod(param1: String, param2: UUID)
}
So, I want the
val type
to be String, which I pass into SecondClass.
Reflection is resolved at runtime, and generic types are resolved at compile time.
The SecondClass is subject to type erasure, so what you want to do is essentially impossible.
Even if you try to work around it with a switch:
val type = AClass::class.java.methods[0].parameters[0].type
val toDeserialize = when(type){
String::class.java -> SecondClass<String>()
else -> SecondClass<Any>()
}
This will bring you no benefit since you won't be able to check the actual type of SecondClass.
Is there a way to check if your session variable contains something... Just like a list has a method "Contains". Is there something similar to that? Some method or something?
Hi you can try casting your session so it can have a type for example
var listofperson = Session["ListofPerson"] as List<string>;
var hasGeorge = listofperson.Contains("George");
When you retrieve items from Session, they are of type System.Object. This means that you don't get any of the actual methods available for the object's real type. You can do so by casting it to the correct type. In this case, it sounds like you're storing a List<string>. So we can use the as operator. If the object is not of that type or was null to begin with, myList will null. Otherwise it will be of the type you specify.
List<string> myList = Session["myKey"] as List<string>();
if(myList == null)
{
//either Session["myKey"] was null or the object wasn't a List<string>
}
else
{
if(myList.Contains("fuzzy puppies"))
{
//your list contains fuzzy puppies
}
else
{
//your list doesn't contain fuzzy puppies
}
}
Calling .ToString() on an object gives you different results based on the object type. The default behavior is to print out the type of the object. But types can override this behavior. For example, calling .ToString() on a string just gives you the string itself. Calling .ToString() on an object that represents some XML might give you the XML as a string. Since List<string> doesn't override the default behavior of System.Object.ToString(), it just prints out "System.Collections.Generic.List`1[System.String]"
First, check if Session["yoursession_var"] is null. Then cast to List(). Then use Exists(), as described here: how to use Exist in List<string> in C#
When using a {} as follows:
var m = {};
Then m is an Object that does not possess the methods of a Dict. You can see by pasting into jsfiddle
var m = {};
m['a'] = 'x';
alert(m.keys());
This will not run - since keys() is not a method on the given object. So then - how to get a dictionary with all its methods?
Update From #SLaks suggestion: Changing the original line to
var m = new Map();
does the trick
There is no such thing as a dictionary in Javascript.
You can use a regular object as a dictionary, as you're doing, and use methods like Object.keys() to help you.
If you use ES6 (or a polyfill), you can use the Map class, which is a normal class with get() and set() methods.
{} is an "object literal". It has no methods or properties other than what's part of the object prototype (a limited set of functions, such as toString, hasOwnProperty, etc), and what you define on it. It is otherwise empty and does not expose functionality you'd expect on a Dictionary. That's where Object comes in.
The static Object reference has an API on it that you can provide your objects to and effectively exposes a set of functions that can be performed on your object as if they were default methods a "dictionary" might expose.
var m = {};
m.a = 'x';
Object.keys(m) // => ['a']
You can find more methods that Object supports on MDN, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
There is no "Dictionary", but an object in JavaScript can be used in a very similar way to a Map in Java.
var myObject = {}
...
for (var key in myObject) {
if (myObject.hasOwnProperty(key)) {
var value = myObject[key];
}
}
The hasOwnProperty() check is to avoid finding keys higher up JavaScripts prototype chain.
I'm in scala writing a serializer that saves an object (or Model) to the database (for app engine), and I need to treat some fields as special cases. For example, if the field is of type Array[Byte], I Save it as a blob. And I need to treat Enumerations as special cases too, but I can't find out how to know if a type is an enumeration.
For example:
object UserType extends Enumeration {
val Anonym, Registered, Admin, Super = Value
}
var value = UserType.Admin
value.isInstanceOf[Enumeration] // this returns false
Neither I can do value.isInstanceOf[Enumeration.Value] since Value is private... anyway I think that would return false too.
Any idea?
Thanks!
value.isInstanceOf[Enumeration$Value]
You could figure this out using these methods:
scala> value.getClass
res102: java.lang.Class[_] = class scala.Enumeration$Val
scala> value.getClass.getSuperclass
res103: java.lang.Class[_ >: ?0] = class scala.Enumeration$Value
scala> value.getClass.getSuperclass.getSuperclass
res104: java.lang.Class[_ >: ?0] = class java.lang.Object