Can you cast an object to a class dynamically? - android

I would like to cast an object based on one of it's fields, the field would have the right value for the object.
for example
object as object.class
object.class would be equivalent to a class, like Int or any other kotlin class.
is it possible?

yes you can do this if your Required class extend the base class
(list.get(pos) as Class).requeiedfunction

With an expected interface and a BaseClass you an accomplish this with type safety as needed. This is using objects as input, but following this pattern any type instance will work.
The interface forces the type to be assigned, and can allow you to maintain Type at scoping without any magic. It allows you to handle all expected cases easily.
interface ClassCast<out T> {
val type: T
}
open class MyClassBase
class MyClassOne : MyClassBase()
class MyClassTwo : MyClassBase()
class LonerClass
val myObjOne = object : ClassCast<MyClassBase> {
override val type = MyClassOne()
}
val myObjTwo = object : ClassCast<MyClassBase> {
override val type = MyClassTwo()
}
val nonRelatedObj = object : ClassCast<LonerClass> {
override val type = LonerClass()
}
fun castToClass(item: ClassCast<Any>): Any {
return when (item.type) {
is MyClassOne -> {
println("We have an Instance of class one")
MyClassOne()
}
is MyClassTwo -> {
println("We have an Instance of class two")
MyClassTwo()
}
is LonerClass -> {
println("Class that doesnt inherit from base type")
LonerClass()
}
else -> {
println("Unexpected type: $item")
throw Exception("Unexpected type cant be cast")
}
}
}
castToClass(myObjOne) // Prints: "We have an Instance of class one"
castToClass(myObjTwo)) // Prints: "We have an Instance of class two"
castToClass(nonRelatedObj) // Prints: "Class that doesnt inherit from base type"

Related

class property becomes null after viewModel calls it in kotlin

class MainAcitvity
fun roomSetup(){
setFavouriteDao = FavouriteDatabase.getDatabase(applicationContext).setFavouriteDao()
repositoryRoom = LorRepository(setFavouriteDao)
viewModelRoom = ViewModelProvider(this,LorViewModelFactory(repositoryRoom!!)).get(LorViewModel::class.java)
}
override fun onMovieClick(position: Int) {
roomSetup()
Toast.makeText(this#MainActivity, "clicked!"+position, Toast.LENGTH_SHORT).show()
var setFavourite = SetFavourite(movieResponse!!.docs[position].Id.toString(),movieResponse!!.docs[position].name.toString())
viewModelRoom.addToFavourites(setFavourite)
}
class ViewModel
fun addToFavourites(setFavourite: SetFavourite){
viewModelScope.launch(Dispatchers.IO){
lorRepository.addToFavourites(setFavourite)
}
}
class LorRepository( favouriteDao: SetFavouriteDao?) {
var favouriteDao : SetFavouriteDao
init {
this.favouriteDao = favouriteDao!!
}
private var lorApi: LORApi.LorCalls? = null
constructor(lorApi2 : LORApi.LorCalls?, favouriteDao: SetFavouriteDao?) : this(favouriteDao){
this.lorApi = lorApi2
}
I have 2 constructors
one to initialize room other for initializing retrofit
I am Also doubtful about the constructor in Repository. Thoose are made for 2 different purposes, one for initializing room database and other for repository. but everytime I create one object of room/retrofit the second constructor , when called, fills it with null values
My questions for you are:
Why do you to initialize retrofit and room's dao in a separate constructor?
What is it that you try to achieve?
In your code you only call to initialize dao constructor, therefore lorApi is null.
For your case you wouldn't want to initialize them separately.
Change your code to this:
class LorRepository(private val lorApi : LORApi.LorCalls, private val favouriteDao: SetFavouriteDao)

In Kotlin, can I apply lint to the constructors of all classes that inherit the class I specify?

class SomeDetector : Detector(), SourceCodeScanner {
override fun getApplicableConstructorTypes(): List<String>? {
return listOf(PARENT_CLASS)
}
override fun visitConstructor(context: JavaContext, node: UCallExpression, constructor: PsiMethod) {
// blabla...
}
}
Ok, I've even succeeded in applying lint to individual constructors for each class.
However, there are hundreds of classes I want to validate, and they all inherit a common interface.
So I want to verify the constructors of all classes inheriting the interface I specified.
The class I want to verify has an android dependency, so libraries like reflections cannot be used directly in the lint module, which is a java-library.
Can you help me to meet my requirements?
I gave your problem a go. I have checked if the value in annotation and the argument used are equal. You can tweak the code to suit your requirement. Here is a sample Detector class wherein I have provided explanations using comments. You can improve on it.
class InvalidConstructorCallDetector : Detector(), Detector.UastScanner {
// Check for call expressions
override fun getApplicableUastTypes() = listOf(UCallExpression::class.java)
override fun createUastHandler(context: JavaContext) = object : UElementHandler() {
override fun visitCallExpression(node: UCallExpression) {
// Check if call is constructor call and if the class referenced inherits your interface
if (node.isConstructorCall() &&
context.doesInherit(node, "com.example.android.YourInterface")
) {
val constructor = node.resolve()!!
// Get the first parameter. You may use a loop and check for all parameter or whatever you require
val param = constructor.parameterList.parameters[0]
// Get your annotation
val paramAnnotation =
param.getAnnotation("com.example.android.YourAnnotation")!!
// Get the value you specified in the annotation for the constructor declaration
val attributeValue = paramAnnotation.findAttributeValue("value")!!.text.toInt()
// Get the argument used for first parameter. Again, you can have this in a loop and use index
val argumentValue = node.getArgumentForParameter(0)!!.evaluate().toString().toInt()
if (attributeValue != argumentValue) // Checking for equality. Perform whatever check you want
{
context.report(
ISSUE,
node,
context.getNameLocation(node),
"Argument value($argumentValue) is invalid. Valid argument: $attributeValue"
)
}
}
}
}
// Check to see if class referenced by constructor call implements interface
private fun JavaContext.doesInherit(node: UCallExpression, typeName: String): Boolean {
for (type in node.classReference!!.getExpressionType()!!.superTypes) {
if (evaluator.typeMatches(type, typeName)) return true
}
return false
}
companion object {
val ISSUE = Issue.create(
"InvalidConstructorCall",
"Invalid arguments in constructor call",
"Only values defined in annotation in constructor declaration are allowed",
Category.CORRECTNESS,
10,
Severity.ERROR,
Implementation(
InvalidConstructorCallDetector::class.java,
EnumSet.of(Scope.JAVA_FILE)
)
)
}
}

Kotlin annotation - Require a parameter is a Constant variable from specific class

I have a function filter here
fun filter(category: String) {
...
}
and a Class with many constant string
object Constants {
val CAT_SPORT = "CAT_SPORT"
val CAT_CAR = "CAT_CAR"
...
}
How to ensure the parameter category is a constant string from Constants (or throw warning)?
I am looking for something like #StringRes.
I know Enum may do the trick but prefer not to code refactor at this moment.
Using androidx.annotation you can do something like this:
object Constants {
#Retention(AnnotationRetention.SOURCE)
#StringDef(CAT_SPORT, CAT_CAR)
annotation class Category
const val CAT_SPORT = "CAT_SPORT"
const val CAT_CAR = "CAT_CAR"
}
fun filter(#Constants.Category category: String) {
...
}

Kotlin: How to access field from another class?

package example
class Apple {
val APPLE_SIZE_KEY: String = "APPLE_SIZE_KEY"
}
Class:
package example
class Store {
fun buy() {
val SIZE = Apple.APPLE_SIZE_KEY
}
}
Error:
'APPLE_SIZE_KEY' has private access in 'example.Apple'
But official documentation describes that if we do not specify any visibility modifier, public is used by default.
Why is above error coming?
What you are trying to do is accessing a value of a class that has no instance. Here are three solutions:
package example
object Apple {
val APPLE_SIZE_KEY: String = "APPLE_SIZE_KEY"
}
This way you do not need to instantiate anything because of the way objects work in Kotlin.
You could also just instantiate your class like this:
package example
class Store {
fun buy() {
val SIZE = Apple().APPLE_SIZE_KEY
}
}
In this solution you also have an object of Apple, but Apple is still declared as a class.
The third option is a companion object, which behaves like static variables in Java.
package example
class Apple {
companion object {
val APPLE_SIZE_KEY: String = "APPLE_SIZE_KEY"
}
}
If you want this to be a class level property instead of an instance level property, you can use a companion object:
class Apple {
companion object {
val APPLE_SIZE_KEY: String = "APPLE_SIZE_KEY"
}
}
fun useAppleKey() {
println(Apple.APPLE_SIZE_KEY)
}
What you currently have is an instance property, which you could use like this:
fun useInstanceProperty() {
val apple = Apple()
println(apple.APPLE_SIZE_KEY)
}

In which situation val/var is necessary in Kotlin constructor parameter?

Right code:
class MainActHandler(val weakActivity: WeakReference<Activity>): Handler() {
override fun handleMessage(msg: Message?) {
val trueAct = weakActivity.get() ?: return
if (msg?.what == ConversationMgr.MSG_WHAT_NEW_SENTENCE){
val sentence = msg.obj as String?
trueAct.conversation.text = sentence
}
super.handleMessage(msg)
}
}
cannot be resolved code:
class MainActHandler(weakActivity: WeakReference<Activity>): Handler() {
override fun handleMessage(msg: Message?) {
val trueAct = weakActivity.get() ?: return
if (msg?.what == ConversationMgr.MSG_WHAT_NEW_SENTENCE){
val sentence = msg.obj as String?
trueAct.conversation.text = sentence
}
super.handleMessage(msg)
}
}
cannot be resolved code screenshot
The only difference is the "val" has been deleted and cannot be resolve.
Which might be important is that it's a inner class.
BUT
This one class without "val/var" in constructor parameter is working:
class BookInfo(convrMgr: ConversationMgr, id: String, queue: RequestQueue, queueTag:String) {
val TAG = "BookInfo"
var title: String? = ""
init {
val url = "https://api.douban.com/v2/book/$id"
// Request a string response from the provided URL.
val stringRequest = StringRequest(Request.Method.GET, url,
Response.Listener<String> { response ->
Log.d(TAG + " Response", response.substring(0))
// Parse JSON from String value
val parser = Parser()
val jsonObj: JsonObject =
parser.parse(StringBuilder(response.substring(0))) as JsonObject
// Initial book title of book properties.
title = jsonObj.string("title")
Log.d(TAG + " Book title", title)
convrMgr.addNewMsg(title)
},
Response.ErrorListener { error -> Log.e(TAG + " Error", error.toString()) })
// Set the tag on the request.
stringRequest.tag = queueTag
// Add the request to the RequestQueue.
queue.add(stringRequest)
}
}
And if I add var/val before "queue: RequestQueue", I'll get suggestion:
"Constructor parameter is never used as a property less. This inspection reports primary constructor parameters that can have 'val' or 'var' removed. Unnecessary usage of 'val' and 'var' in primary constructor consumes unnecessary memory."
I am just confused about it.
When you write val/var within the constructor, it declares a property inside the class. When you do not write it, it is simply a parameter passed to the primary constructor, where you can access the parameters within the init block or use it to initialize other properties. For example,
class User(val id: Long, email: String) {
val hasEmail = email.isNotBlank() //email can be accessed here
init {
//email can be accessed here
}
fun getEmail(){
//email can't be accessed here
}
}
Constructor parameter is never used as a property
This suggestion is saying that you do not use this property in place apart from the initialization. So, it suggests you to remove this property from the class.
Constructor parameters must use var or val when they are used as a property elsewhere in the class. They do not need to be properties if they are only used for class initialization.
In the example below, the parameter must be a property (var or val) because it is used in a method:
class A(val number: Int) {
fun foo() = number
}
In this other example, the parameter is only used to initialize the class, so it does not need to be a property:
class B(number: Int): A(number) {
init {
System.out.println("number: $number")
}
}
This might be a late answer but the magic lies under the hood:
Based on #BakaWaii's answer:
Putting var/val will make the variable a property of the class and not putting it will make it a parameter of only the constructor function.
So what does it mean, to understand lets look into some code:
class Test(a: Int){}
Now Lets see the decompiled java code:
public final class Test {
public Test(int a) {
}
}
So now if I try to access a using the object of Test() like the below code:
Test t = new Test(10);
t.a //Error
It will give me error. Unresolved reference: a. Why because a is a parameter of the constructor only.
Now if we put var/val in the paramater like below:
class Test(var a: Int){}
The decompliked Java code will become:
public final class Test {
private int a;
public final int getA() {
return this.a;
}
public final void setA(int var1) {
this.a = var1;
}
public Test(int a) {
this.a = a;
}
}
Thus it will not only give you a class property but also give you getter/setters for setting the values.
Now the next question arises if the field a is private how can it be accessed. Simple answer in Java you cannot, i.e. if you are calling the KT class from a Java you will not be able to assign value of a like Test(1).a = 10 but will have to use Test(1).setA(5).
But as kotlin internally handles getters/setters Test(1).a = 5 will be ok.
For #Parcelize to work you need to open up the super's properties and override them in the child:
abstract class Goal(open var number: Int, open var name: String) : Parcelable
#Parcelize
class OperationalGoal(override var number: Int, override var name: String, var description: String) : Goal(number, name)```
In very simple terms, use var or val in class constructor parameters when you want to use that variable, say, inside a method within that class. Thus you're effectively turning them into properties and not just mere constructor or method parameters.
class User(var name: String, age: Int) {
var str = "John"
var num = 18
fun setName(){
name = str // due to using var on our class constructor parameter, we can access the constructor variable *name* inside this setter method. *name* is a property parameter thanks to the var keyword.
}
fun setAge(){
age = num // this will result in a compiler error, because *age* is just a parameter, notice that var wasn't used in the *age* parameter within the class constructor, which means we can't access it like we did with *name*
}
}
Run this Kotlin Playground code to get a clearer idea of what's going on.

Categories

Resources