Kotlin: How to access field from another class? - android

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)
}

Related

how to write Android Custom lint rule to forbid calling specific function in all classes that extends specific type?

I want to write a custom lint rule to ban calling function states.accept() in all classes that extends BaseViewModel where states is a BehaviorRelay object.
how can I achieve something like this.
I’ve written the check using visitMethodCall but this only can check the function name and if it’s member of BehaviorRelay,
the missing part is how to check if this function is being called in children’s of BaseViewModel.
below is the part that works: using visitMethodCall but detecting the function in whole code.
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
val evaluator = context.evaluator
if (evaluator.isMemberInClass(method, BEHAVIOR_RELAY)) {
if (method.name == ACCEPT_FUNCTION) {
context.report(
Incident(
issue = ISSUE,
scope = node,
location = context.getNameLocation(node),
message = "View Models implements `BaseViewModel` must not update `states`"
)
)
}
}
}
applicableSuperClasses will filter only the classes that extends passed types, in my case BaseViewModel. this function works with visitClass.
Then using AbstractUastVisitor() to visit all calls in that class and find the specific function by name, also checking if it's a member function in target type.
full working code.
override fun applicableSuperClasses(): List<String>? {
return listOf(BASE_VIEW_MODEL)
}
override fun visitClass(context: JavaContext, declaration: UClass) {
val evaluator = context.evaluator
declaration.accept(object : AbstractUastVisitor() {
override fun visitCallExpression(node: UCallExpression): Boolean {
val isRelayFunction = evaluator.isMemberInClass(
node.resolve(),
BEHAVIOR_RELAY
)
if (node.methodName == ACCEPT_FUNCTION && isRelayFunction) {
context.report(
issue = ISSUE,
scope = node,
location = context.getNameLocation(node),
message = "View Models implements `BaseViewModel` must not update `states`"
)
}
return super.visitCallExpression(node)
}
})
}

Reference an object in a class by using a string?

I want to reference an object within this class I have below:
class HerbData {
object Dill {
const val herbName: String = "This is Dill!"
const val scientificName: String = "Anethum Graveolens"
val dullThumbnail: Int = R.drawable.dill_thumbnail_attr
}
object Peppermint {
val herbName: String = "This is Peppermint!"
}
}
Is there anyway that I can reference the object by using a string in Kotlin? Here is somewhat what I mean:
HerbData."Dill".herbname
I can't find anything on this topic for Kotlin.
Another way you could do this is with an enum class. The advantage over a map is that you have a data structure you can reference directly in code, so you could use HerbData.Dill as well as HerbData["Dill"]. And that will enable you to take advantage of compile-time checking and lint warnings, refactoring, exhaustive pattern matching, code completion etc, because the data is defined in your code
enum class HerbData(
val herbName: String,
val scientificName: String? = null,
val dullThumbnail: Int? = null
) {
Dill("This is Dill!", "Anethum Graveolens", R.drawable.dill_thumbnail_attr),
Peppermint("This is Peppermint!");
companion object {
operator fun get(name: String): HerbData? =
try { valueOf(name) } catch(e: IllegalArgumentException) { null }
}
}
fun main() {
// no guarantee these lookups exist, need to null-check them
HerbData["Peppermint"]?.herbName.run(::println)
// case-sensitive so this fails
HerbData["peppermint"]?.herbName.run(::println)
// this name is defined in the type system though! No checking required
HerbData.Peppermint.herbName.run(::println)
}
>> This is Peppermint!
null
This is Peppermint!
Enum classes have that valueOf(String) method that lets you look up a constant by name, but it throws an exception if nothing matches. I added it as a get operator function on the class, so you can use the typical getter access like a map (e.g. HerbData["Dill"]). As an alternative, you could do something a bit neater:
companion object {
// storing all the enum constants for lookups
private val values = values()
operator fun get(name: String): HerbData? =
values.find() { it.name.equals(name, ignoreCase = true) }
}
You could tweak the efficiency on this (I'm just storing the result of values() since that call creates a new array each time) but it's pretty simple - you're just storing all the enum entries and creating a lookup based on the name. That lets you be a little smarter if you need to, like making the lookup case-insensitive (which may or may not be a good thing, depending on why you're doing this)
The advantage here is that you're generating the lookup automatically - if you ever refactor the name of an enum constant, the string label will always match it (which you can get from the enum constant itself using its name property). Any "Dill" strings in your code will stay as "Dill" of course - that's the limitation of using hardcoded string lookups
The question really is, why do you want to do this? If it's pure data where no items need to be explicitly referenced in code, and it's all looked up at runtime, you should probably use a data class and a map, or something along those lines. If you do need to reference them as objects within the code at compile time (and trying to use HerbData."Dill".herbName implies you do) then an enum is a fairly easy way to let you do both
Declare a Data Class
data class HerbData (
val scientificName: String,
val dullThumbnail: Int
)
Initialize a muteable map and put data in it
val herbData = mutableMapOf<String, HerbData>()
herbData.put("Dill", HerbData("Anethum Graveolens", R.drawable.dill_thumbnail_attr))
herbData.put("Peppermint", HerbData("Mentha piperita", R.drawable.peppermint_thumbnail_attr))
You can now just
herbData["Dill"]?.scientificName
class HerbData {
interface Herb {
val herbName: String
val scientificName: String
}
object Dill : Herb {
override val herbName: String = "This is Dill!"
override val scientificName: String = "Anethum Graveolens"
}
object Peppermint: Herb {
override val herbName: String = "This is Peppermint!"
override val scientificName: String = "Mentha piperita"
}
companion object {
operator fun get(name: String): Herb? {
return HerbData::class
.nestedClasses
.find { it.simpleName == name }
?.objectInstance as? Herb
}
}
}
println(HerbData["Dill"]?.herbName) // Prints: This is Dill!
println(HerbData["Peppermint"]?.scientificName) // Prints: Mentha piperita
println(HerbData["Pepper"]?.herbName) // Prints: null

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 - Inheritance in Object expression

I need to inherit the Object A from Object B, were both the objects consist of constants only.
Example
Object A {
const val a1 = "some_data_1"
const val a2 = "some_data_2"
}
Object B : A {
const val b1 = "some_data_3"
}
is it feasible to achieve this in kotlin ?
Kotlin is an object-oriented programming (OOP) language. We can inherit object A from object B for that we have to to allow class "A" to be inherited, for that we need to attach the open modifier before the class to make it non-final.
For the const we have to use companion object, which is an object that is common to all instances of that class.
open class A {
companion object {
const val a1 = "some_data_1"
const val a2 = "some_data_2"
}
}
class B : A() {
companion object {
const val b1 = "some_data_3"
}
val a_1 = a1
val a_2 = a2
}
Check this link to understand inheritance
Check this link to understand Companion Object
open class A {
companion object {
const val a1 = "some_data_1"
const val a2 = "some_data_2"
}
}
class B : A() {
companion object {
const val b1 = "some_data_3"
}
val a_1 = a1
val a_2 = a2
}
for a class to be inherited in Kotlin it should be open for example open class A {}
for class B to extends class A should add the class B : A()
for constants should be inside a companion object {}
I would probably dive a bit deeper.
Object in your example is an Object Declaration.
You should have a look at this doc describing Object Declarations and Object Expressions.
The question is - Why would you need to have one class only with constants extend another(also containing only const vals)?
Object Declarations are Kotlin built in Singletons and BTW are thread safe.
Example :
object DeviceProvider {
private val _devices = ArrayList<Device>()
fun getDevices() = _devices as List<Device>
fun registerDevice(device: Device) {
_devices.find { it == device } ?: _devices.add(device)
}
}
Usage :
fun addDevice(){
ServiceProvider.registerDevice(Device("1234"))
}
Object declarations are allowed to extend open classes and interfaces - so you can define a behavior or even a state via inheritance. As usual you can have a look at Kotlin docs about inheritance, those are exhaustive and nice read.
Still if we are talking about common approaches defining const values - then separate file is the best solution, if of course that value should be bound to any specific class. Here is a nice point of view(thanks Marko for your answer) :
In Java you're forced to put all static field and method declarations
in a class and often you even have to create a class just for that
purpose. Coming to Kotlin, many users look for the equivalent facility
out of habit and end up overusing companion objects.
Kotlin completely decouples the notions of a file and a class. You can
declare any number of public classes is the same file. You can also
declare private top-level functions and variables and they'll be
accessible only to the classes within the same file. This is a great
way to organize closely associated code and data.

Kotlin easily access resources outside of an activity

I'm new at Android and Kotlin. I'm trying to create a ResourcesHelper class to easily access my custom colors and fonts from any other custom class in my app. But in this helper I don't have any context. I've read ways to get the context extending the Application class but then compiler says I can't access this context in my ResourcesHelper companion object as it would create memory leaks. Also I ended up with optional chained.
Here is how I would like to be able to use it :
class ResourcesHelper {
companion object {
val lightBlue = resources.getColor(R.color.lightBlue)
val customBlue = resources.getColor(R.color.customBlue)
// [...]
val fontAwesome = resources.getFont(R.font.fontawesome)
val lemonMilk = resources.getFont(R.font.lemonmilk)
}
}
enum class ButtonStyle {
MENU,
// [...]
VICTORY
}
class CustomButton(c: Context, attrs: AttributeSet) : Button(c, attrs) {
var isButtonActivated = false
fun setStyle(style: ButtonStyle) {
setBackgroundColor(ResourcesHelper.transparent)
when(style) {
ButtonStyle.MENU -> {
setText(R.string.menu_button)
typeface = ResourcesHelper.lemonMilk
setBackgroundColor(ResourcesHelper.customRed)
setTextColor(ResourcesHelper.white)
}
// [...]
ButtonStyle.VICTORY -> {
setText(R.string.victory_button)
typeface = ResourcesHelper.lemonMilk
setBackgroundColor(ResourcesHelper.customRed)
setTextColor(ResourcesHelper.white)
}
}
}
}
I also read this post Android access to resources outside of activity but it's in Java and I have no idea on how to do it in Kotlin.
I am completely lost on what and how to do this... Or if there is a better way to achieve reaching resources from anywhere.
Thanks for your help
For Color, String, etc system resources you can use the Resources class, as shown below
import android.content.res.Resources
class ResourcesHelper {
companion object {
val lightBlue = Resources.getSystem().getColor(R.color.lightBlue)
}
}
If you want to support different Android versions I'll recommend to use ContextCompat. It provides unified interface to access different resources and backward compatibility for older Android versions.
For AnroidX use androidx.core.content.ContextCompat, for SupportV4: android.support.v4.content.ContextCompat.
val lightBlue = ContextCompat.getColor(context, R.color.lightBlue)
val customBlue = ContextCompat.getColor(context, R.color.customBlue)

Categories

Resources