I'm going to set variables in class Data based on the arguments of DClass
abstract class Data(p: String) {
var a1: String
var a2: String
init {
"""(\w+)(\d+)""".toRegex().find(p)!!.groupValues.run {
a1 = "a1 is ${get(1)}"
a2 = "a2 is ${get(2)}"
}
}
}
data class DClass(val p1: String, val p2: String) : Data(p1)
Then I'm able to get values of a1 and a2 after DClass is created:
DClass("string1", "string2").run { println("$a1 $a2") }
it returns "a1 is string a2 is 1" as it should
Next I'm trying to make retrofit initialize DClass from the proper JSON response:
#GET("loadDClass") suspend fun dClass(): DClass
and say the program to do the same output of a1 and a2, it just returns:
"a1 is null a2 is null".
So I found out that init{} block of Data is skipped as the retrofit just builds DClass but doesn't do the initialization
Is it possible to make abstract class' variables be initialized without doing it manually?
The init block is not called because GsonConverterFactory (which I assume you use since it is very common in this case) constructs the DClass in a special way that does not invoke its constructor. In particular, an internal class called UnsafeAllocator is used. The JavaDoc for that class states:
/**
* Do sneaky things to allocate objects without invoking their constructors.
* [...]
*/
You could write your own Retrofit Converter.Factory but that would IMO be overkill. I suggest you add a simple member function to DClass instead of inheriting from Data, and also remove the abstract keyword from Data. DClass should look like this:
data class DClass(val p1: String, val p2: String)
fun newData(): Data = Data(p1)
}
then you can construct a Data instance from the body() of the response:
call.execute().body()?.newData()?.run { println("$a1 $a2") }
Related
i'm new to kotlin android studio,trying to update the textView with editText from data class and i used in editText "afterTextChanged" , created a setter function in data class with editable paramete, i can't convert double to string with editable, anyone help me out ?
here is function in data class
fun setSize(editable: Editable) {size = editable.toString()}
Your size variable is (apparently) a Double, so you have to assign a Double value to it. You're doing editable.toString() which just gives you a String representation of your Editable's contents.
You need to try and parse that as a Double with toDouble() (which throws an exception if it's not a valid number) or toDoubleOrNull (which returns null if it's invalid).
Since it's (I assume) user-edited content you're parsing, and there's a good possibility they'll enter something that's not a valid number representation, you need to handle that possibility. I'd go with toDoubleOrNull since it's easier in Kotlin (there are lots of these *OrNull variants on functions for that reason):
fun setSize(editable: Editable) {
// only runs the let block if the value is parsed
editable.toString().toDoubleOrNull()?.let { size = it }
}
or if you're not familiar with let:
fun setSize(editable: Editable) {
val double = editable.toString().toDoubleOrNull()
if (double != null) size = double
}
I'm making a project with many functions, each named prx, x being a number up to 200, is it possible to create a call from a string like this
var ="pr" + x
You can do this using reflection
class MyTestClass {
fun pr1(): Int = 5
fun pr2(value: Int) = value.toString()
}
class SomeOtherClass{
fun main(){
val myTestClassObj = MyTestClass()
// Find pr1 by name (function without arguments)
val pr1 = myTestClassObj::class.java.getMethod("pr1").kotlinFunction
// call pr1, pass class object as argument, used to call the function
val pr1Result = pr1?.call(myTestClassObj)
// Find function with arguments, pass function name and type of arguments
val pr2 = MyTestClass::class.java.getMethod("pr2", Int::class.java).kotlinFunction
// Call pr2, pass class reference and the function parameter
val pr2Result = pr2?.call(myTestClassObj, 100)
}
}
Be carefull when working with reflection, its easy to create untraceable bugs using it and often it is only a workaround for bad design.
For example, given this groovy code:
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
import java.lang.annotation.ElementType
import java.lang.annotation.Target
#Target([ElementType.PARAMETER])
#Retention(RetentionPolicy.RUNTIME)
#interface Bar {
String qux() default ""
}
def closure1 = { #Bar(qux = 'zxv') String foo ->
println foo
}
println "[value for qux]"
How would you print the value for qux (which in this case is 'zxv') ?
Reflection API can give you what you're looking for (I'm using Groovy 2.5 on Java 8).
The closure is essentially a method named call on a Closure instance, and parameterAnnotations[0][0] is a quick way of getting that method's first parameter's first annotation.
def anno = closure1.class.methods.find { it.name == 'call' }.parameterAnnotations[0][0]
println anno.qux() // prints zxv
In the documentation/section 14.4 I came accross with the following example of code:
task configure << {
def pos = configure(new java.text.FieldPosition(10)) {
beginIndex = 1
endIndex = 5
}
println pos.beginIndex
println pos.endIndex
}
It's not quite clear what the pos and the configure means. I thought configure is just a property, so we can write something like
println configure.beginIndex
but the line causes compile-time error. And
{
beginIndex = 1
endIndex = 5
}
is just a closure, is it?
configure() is a method of the gradle Project object. The documentation of this method explains what it does:
Object configure(Object object, Closure configureClosure)
Configures an object via a closure, with the closure's delegate set to the supplied object. This way you don't have to specify the context of a configuration statement multiple times.
Instead of:
MyType myType = new MyType()
myType.doThis()
myType.doThat()
you can do:
MyType myType = configure(new MyType()) {
doThis()
doThat()
}
So the manual snippet defines an object of type FieldPosition, assigns it to the variable pos, sets its beginIndex and endIndex properties using a closure, thanks to the configure() method of Project, and then prints these properties.
It's a pointless example showing how to use the gradle DSL to configure several properties of an object.
I have the following toplevel class:
class Example(shared String first = "default one", shared String second = "default two") {
}
Now, I want to instantiate this class using an explicit value for first, but the default value for second.
I know how to do this via explicitly compiled code, by using named arguments:
void instantiateExample(String firstValue) {
Example ex = Example { first = firstValue; };
assert(ex.first == firstValue);
assert(ex.second == "default two");
}
Now, I would like to do the same thing as the above code, but by using the Class<Example, []|[String,String=]> object.
with class model it is ...
Class<Example,[]|[String, String=]> exampleModel = `Example`;
value e1 = exampleModel();
value e2 = exampleModel("foo");
value e3 = exampleModel("foo", "bar");
or with class declaration it is ...
ClassDeclaration exampleDeclaration = `class Example`;
assert(is Example e1 = exampleDeclaration.instantiate());
assert(is Example e2 = exampleDeclaration.instantiate([], "foo"));
assert(is Example e3 = exampleDeclaration.instantiate([], "foo", "bar"));
HTH