How do I create an instance of a Class passed as parameter to a function? - kotlin

I'm building a library in Kotlin and here's my usecase
I have a base class
abstract class Component(...) {
// ... class body
}
I want the users of my library to define their own sub-classes like say:
class MyComponent() : Component() {
// .. class body
}
How can I write a helper function that takes in this derived class as a param and create an instance out of it. Something like:
fun helper(component: Class, props: HashMap<String, String>) : Component {
// somehow create a new instance of Class and return it?
}
Thanks!

You can have users pass a constructor reference:
fun helper(componentConstructor: ()->Component, props: Map<String, String>) : Component {
val component = componentConstructor()
// set it up and return it.
}
// usage:
val component = helper(::MyComponent, emptyMap())
Better for props not to require a specific type of map since it doesn’t matter here. Needless burden for users of your library.

abstract class Component(val prop1: String, val prop2: String) {
// ... class body
}
class MyComponent(prop1: String, prop2: String) : Component (prop1, prop2) {
// ... class body
}
fun helper(component: Class<MyComponent>, props: Map<String, String>): Component {
val constructor = component.constructors.first { it.parameterCount == props.size }
val arguments = props.values.toTypedArray()
return constructor.newInstance(*arguments) as Component
}
val instance = helper(MyComponent::class.java, mapOf("prop1" to "value1", "prop2" to "value2"))
println(instance.prop1 + ", " + instance.prop2) // Prints: value1, value2

Related

what is default value type of kotlin class constructor parameter?

class Greeter(name: String) {
fun greet() {
println("Hello, $name")
}
}
fun main(args: Array<String>) {
Greeter(args[0]).greet()
}
for above program I got this error
Unresolved reference: name
but when I add var or val
class Greeter(var name: String) {
or
class Greeter(val name: String) {
then program works fine, so why I need to add var or val to name, what is default type for constructor parameter val or var and why program gives me error when I not mention var or val
To use your value in the constructor like class Greeter(name: String), you can use init{}
class Greeter(name: String) {
var string:name = ""
init{
this.name = name
}
fun greet() {
println("Hello, $name")
}
}
or If you use val or var in the constructor it is more like class level variable and can be accessed anywhere inside the class
class Greeter(var name:String){
fun greet() {
println("Hello, $name")
}
}
The variable name can be used directly in the class then.
We can also give default values for the variables in both cases.
Adding val or var makes the parameter a property and can be accessed in the whole class.
Without this, it is only accessible inside init{}
The question is not making any sense, But the problem you are facing does make sense. In your case, the approach you are using is,
Wrong-Way:
// here name is just a dependency/value which will be used by the Greeter
// but since it is not assigned to any class members,
// it will not be accessible for member methods
class Greeter(name: String) {
fun greet(){} // can not access the 'name' value
}
Right-Way:
// here name is passed as a parameter but it is also made a class member
// with the same name, this class member will immutable as it is declared as 'val'
class Greeter(val name: String) {
fun greet(){} // can access the 'name' value
}
You can also replace val with var to make the name a mutable class member.

How to instantiate class in Kotlin, if the class is parameter of function?

Im trying to make universal function in Kotlin, which can instantiate every time different model classes.
Class type is a parameter, to make instance from that class and fill it with data from Json object.
fun<T> foo() {
var myModel = T()
myModel.id = 2
myModel.name = ""
}
You can use an inline reified function in combination with an interface.
With reified you can access the real class of the generic type parameter.
You still not allowed to call the constructor directly, but reflection will work.
To assign the id and name, you need to define an interface, that all of your model classes are required to implement:
interface Model {
var id: Int?
var name: String?
}
inline fun <reified T : Model> createModel() : T {
val myModel = T::class.createInstance()
myModel.id = 2
myModel.name = ""
return myModel
}
A simple example:
class TestModel() : Model {
override var id: Int? = null
override var name: String? = null
}
fun main() {
val model: TestModel = createModel()
}
You cannot use T definition itself, pass class to the function instead.
import kotlin.reflect.KClass
open class Model {
var id: Int? = null
var name: String? = null
fun say() {
println("hello.")
}
}
class MyModel: Model()
fun<T: Model> foo(type: KClass<T>): T {
val myModel = type.java.newInstance()
myModel.id = 2
myModel.name = ""
return myModel
}
val mymodel = foo(MyModel::class)
mymodel.say() // hello.

Hiding base class constructor parameters in Kotlin

I am trying to understand how to hide a base constructor parameter in a subclass in kotlin. How do you put a facade over a base constructor? This doesn't work:
import com.android.volley.Request
import com.android.volley.Response
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private fun hiddenListener() = Response.ErrorListener {
/* super secret listener */
}
...
}
I think I understand the problem:
During construction of a new instance of a derived class, the base
class initialization is done as the first step (preceded only by
evaluation of the arguments for the base class constructor) and thus
happens before the initialization logic of the derived class is run.
I'm trying to solve this problem for Volley, where I need my custom request to be be a Request so that it can be passed into a RequestQueue. It would be easier of RequestQueue took in some kind of interface but since it doesn't I have to subclass. There are other ways I can hide these complexities from the caller, but this limitation has come up for me other times in Kotlin and I'm not sure how to solve it.
I am not familiar with volley but I tried to come up with an example that should give you some insight how to solve your problem. What you can do is use a companion object:
interface MyListener {
fun handleEvent()
}
open class Base<T>(anything: Any, val listener: MyListener) { // this would be your Request class
fun onSomeEvent() {
listener.handleEvent()
}
}
class Derived(anything: Any) : Base<Any>(anything, hiddenListener) { // this would be your MyCustomRequest class
private companion object {
private val hiddenListener = object : MyListener {
override fun handleEvent() {
// do secret stuff here
}
}
}
}
So if you apply this to your problem, the result should look something like this:
class MyCustomRequest(url: String)
: Request<String>(Request.Method.POST, url, hiddenListener) {
private companion object {
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
}
...
}
A different way would be to use a decorator, create your Request withing that decorator and just delegate the calls to it:
class Decorator(anything: Any) {
private var inner: Base<Any>
private val hiddenListener: MyListener = object : MyListener {
override fun handleEvent() { }
}
init {
inner = Base(anything, hiddenListener)
}
}
And once again for your example that would look like this:
class MyCustomRequest(url: String) {
private var inner: Request<String>
private val hiddenListener = Response.ErrorListener {
/* super secret listener */
}
init {
inner = Request<String>(Request.Method.POST, url, hiddenListener)
}
...
}

Kotlin secondary constructor with generic type

In java
I can achieve two constructors like
public TargetTitleEntryController() { }
public <T extends Controller & TargetTitleEntryControllerListener> TargetTitleEntryController(T targetController) {
setTargetController(targetController);
}
I want to convert it to Kotlin
class TargetTitleEntryController ()
with the secondary constructor. I don't know how to declare with generic type like Java counterpart.
There is no intersection types in Kotlin (sad)
But there is Generic constraints (hope)
But Generic constraints not applicable in the secondary constructor (sad)
But you can simulate secondary constructor in a companion object using Invoke operator overloading (workaround):
class TargetTitleEntryController {
// ...
companion object {
operator fun <T> invoke(targetController: T): TargetTitleEntryController
where T : Controller,
T : TargetTitleEntryControllerListener {
return TargetTitleEntryController().apply {
setTargetController(targetController)
}
}
}
}
Here is an example where you specify a Type T which implements two interfaces (CharSequence, Runnable):
class Person<T>(val name: String) where T : CharSequence, T : Runnable {
constructor(name: String, parent: T) : this(name) {
}
}
So actually something like this should work:
class TargetTitleEntryController<T> () where T : Controller, T : TargetTitleEntryControllerListener {
constructor(targetController: T) : this() {
}
}
You can do it like this :)
class TargetTitleEntryController <T>() : Controller() where T: Controller, T: TargetTitleEntryControllerListener<T> {
constructor(target: T) : this() {
targetController = target
}
}
you can implement it in your parent controller like this:
class TargetDisplayController : Controller(), TargetTitleEntryControllerListener<TargetDisplayController> {
var targetTitleEntryController = TargetTitleEntryController(this)
override fun onTitlePicked(String option) {
}
override fun onAttach(view: View) {
// push controller here
}
}

How to get kotlin function's caller

With this example:
open class Parent {
fun some():Parent {
return this;
}
}
class A : Parent(){
val name:String? = null;
}
But then this code results in an error:
val a = A().some().some()
a.name // ERROR
EDITOR NOTE: based on comments of the author to answers below, the question is NOT about referencing a.name but really is about something like "how do I get the instance of the class or its name that first started the chain of method calls". Read all comments below until the OP edits this for clarity.
my final goal is to return caller's type and can call this caller's instance property, no more as , no more override, any idea?
Just like java, you can use stackTrace's getMethodName(). Refer to the kotlin doc.
Actially your example is working(if you add open keyword because all classes in Kotlin are final by default:
A.kt
open class A {
fun some(): A {
return this
}
}
B.kt
class B : A() {
val test = "test"
}
And usage
val tmpB = (B().some().some() as B)
val test = tmpB.test
Edited:
It because function some() return parent class which doesn't have child class property. So you need to cast it to child class.(Update code)
open class Parent{
open fun foo(): Parent {
return this;
}
}
This is your Parent class. Parent class has a method named foo(). foo() is a method of class A which will return the instance of it's own class. We must have to open the class and method because by default their visibility modifier is final.
class A : Parent() {
override fun foo(): A { return this }
}
This is a class named A which extends Parent class. foo() is a method of class A which will return the instance of it's own class.
We will call it like this:
var a = A().foo().foo()
Your class always return Parent instance. This class do not have any field with the name name. To do that you have 2 ways:
The first:
open class Parent{
fun some():Parent{
return this
}
}
class A :Parent(){
val name:String? = null
}
fun main() {
val a = (A().some().some() as A)
a.name = "";
}
The second:
open class Parent{
open fun some():Parent{
return this
}
}
class A :Parent(){
override fun some():A {
return this
}
val name:String? = null
}
fun main() {
val a = A().some().some()
a.name = "";
}
i have know how to do this:
#Avijit Karmakar
#Trần Đức Tâm
use inline function
inline fun <reified T:Any> some(sql: String):T {
return this as T ;
}