Abstract val with annotation in kotlin android - android

Can I write:
#IdRes
abstract fun getHeaderId(): Int
With a val instead of a fun in kotlin? It complains I need a backing field or delegate when i write:
#IdRes <-- errors
abstract val headerId: Int
Which is the most idiomatic in this case? One-liner with a fun or mess around with a backing field (I'm not used to backing fields, maybe it's change-resistance, i have never really used them so i think they are unpleasant)

Since abstract val or var is just a function without a backing field it cannot be annotated by IdRes annotation but there is a workaround. You can use it like this:
#get:IdRes
abstract val headerId: Int
EDIT:
Why does this works? We need to closer inspect IdRes annotation and its source code:
#Documented
#Retention(CLASS)
#Target({METHOD, PARAMETER, FIELD, LOCAL_VARIABLE})
public #interface IdRes {
}
As we can see this annotation can be used on methods, parameters, fields and local variables. When we use abstract val it's neither of those since it is abstract and we cannot have abstract fields in Java. Normally equivalent of abstract val something: Int in Java is:
private int something
public int getSomething() {
return something;
}
From example, it's easy to see that the private field is what is called backing field of a property and you can't have those as abstract so that was the problem.

As mentioned in #AtulGupta comment, #theKarlo 's answer does not force the subclass to pass in an IdRes.
Therefore, an alternative to
#IdRes
abstract fun getHeaderId(): Int
and
#get:IdRes
abstract val headerId: Int
Is to pass the value into the constructor of the class itself, so that the backing field issue can be avoided and the subclass is forced to pass in an IdRes.
For example:
abstract class SomeClass(#IdRes val idRes: Int)

Related

How to define an interface in Kotlin that uses #DrawableRes annotations?

I want to create an interface to define a group of drawables needed for a mic state, something like:
interface MicrophoneState {
val iconResource: Int
val backgroundResource: Int
}
since it should be a resource id I wanted to decorated it with #DrawableRes but I get an error when I do that:
This annotation is not applicable to target 'member property without backing field or delegate'
Is there a way to fix that problem? all the question I have found are about classes but not interfaces.
As it's a state use of data class is ideal
data class MicrophoneState (
#DrawableRes val iconResource: Int,
#DrawableRes val backgroundResource: Int
)
or if you want to use interface
interface MicrophoneState {
#DrawableRes fun iconResource(): Int
#DrawableRes fun backgroundResource(): Int
}
Any one will work.
Update :
I think you are looking for this exactly :
interface MicrophoneState {
#get:DrawableRes val iconResource:Int
#get:DrawableRes val backgroundResource:Int
}
I don't think you need an interface as apparently you're trying to build a model to store data?
class HelloWorldClass (#DrawableRes val value1: Int, #DrawableRes val value2: Int)
(you can as well go with data class)
Interface is a contract for something:
interface HelloWorldInterface {
fun myMethod(#DrawableRes value: Int)
}

Can I execute inherited static functions from a BaseClass from a ChildClass and get the childs properties?

I have a few cases where I want to add static functions or values in a base class so that I can use them in all subclasses that inherits from it.
One such case is when i want to create generic tags for each class to use in data mapping as a key, like when i want to find fragments or pass data between activities.
For example:
open class BaseClass(){
companionObject{
val TAG: String = this.javaClass.simpleName
}
}
class ChildClass: BaseClass()
class Main: Activity(){
fun startActivity(){
val intent = Intent(this, ChildClass::class.java)
intent.putExtra(ChildClass.TAG, data)
startActivity(intent)
finish()
}
}
Can this be done or am I forced to create an companion object for each class?
I don't know a solution with companions. But you could use a global reified inline function for the specific use case, you mentioned in your question:
open class BaseClass()
class ChildClass: BaseClass()
inline fun <reified T> tagOf() = T::class.java.simpleName
fun main(args: Array<String>) {
println(tagOf<BaseClass>())
println(tagOf<ChildClass>())
}
Hm... I think, you can't do it. As mentioned in this article: https://proandroiddev.com/a-true-companion-exploring-kotlins-companion-objects-dbd864c0f7f5
companion object is really a public static final class in your BaseClass. So, I think, you can't do this.

Kotlin Inheritance with Boolean generics

I have a problem when I try to override a generics method with Boolean,Double,Integer,Float.
It works with Date. (May because is it Serializable?)
The interface:
interface AnInterface<C, T> {
fun doSomething(items: List<T>, vararg value: C): List<T>
}
An abstact implementation: (No override doSomething)
abstract class BaseClass<C, T> : AnInterface<C, T> { ... }
It's work:
class AnImplementetion<T> : BaseClass<Date, T>() {
override fun doSomething(items: List<T>, vararg value: Date): List<T> {
// It works
}
}
It doesn't work:
class AnAnotherImplementetion<T> : BaseClass<Boolean, T>() {
override fun doSomething(items: List<T>, vararg value: Boolean): List<T> {
// It doens't
}
}
The IDE always want to implement the doSomething. When I implement it with IDE it creates always the same one.
Error message:
Class 'AnAnotherImplementetion' is not abstract and does not implement abstract base class member
public abstract fun fun doSomething(items: List<T>, vararg value: Boolean): List<T> defined in BaseClass
'doSomething' overrides nothing
How can I fix it?
Thank you
UPDATE:
It works with JAVA. But Why doesn't with Kotlin?
public class AnAnotherImplementetion<T> extends BaseClass<Boolean, T> {
#NotNull
#Override
public List<T> doSomething(#NotNull List<? extends T> items, Boolean... value) {
// It works with JAVA
}
}
UPDATE 2:
It works when vararg is nullable.
interface AnInterface<C, T> {
fun doSomething(items: List<T>, vararg value: C?): List<T>
}
It looks like a bug in kotlin compiler. As I know during compiling it decides to use primitive type (int) or wrap (Integer). Java generics can't work with primitives, so compiler uses wrap for generic-type, BUT then compiler sees, that method param is never null and replaces it with primitive-type, and type-conflict appears. And here nullable saves a day.
But I'm not sure, it's just a guess.
The kotlin reference regarding basic types contains a passage which explains how it should deal with primitive types and generics in particular.
Obviously the latter is not working correctly. When generics are involved it should box the types which it either doesn't do or which the compiler complains about falsely.
You should open a bug at https://youtrack.jetbrains.com/ and link it here too. Maybe it was also a conscious design decision.
A workaround is to use the nullable type Boolean? as that will work as it is described in the reference. It will be boxed and therefore will work with generics.
Alternatively, if you are on the JVM, you can use the java.lang.Boolean instead. It's the object type of the primitive boolean and even though it is discouraged to use the Java types in Kotlin it is a possible workaround until Kotlin behaves as it should. However... testing it, Kotlin does some more magic around it so that its usage isn't that helpful neither. You would then even need to cast the java.lang.Boolean.TRUE as java.lang.Boolean. That's clearly not helpful at all. Opening a bug is the best you can do here.

Is it safe to call abstract methods from constructor in kotlin?

I am working on an android project. I want to make an abstract subclass of FrameLayout with an abstract method
#LayoutRes
abstract fun getLayoutToInflate(): Int
In the constructor I want to inflate the layout returned by this method. But the IDE shows a warning about "Calling non-final function in constructor..." at this code
val inflater = LayoutInflater.from(context)
inflatedBanner = inflater.inflate(getLayoutToInflate(), this, true)
This app doesn't build yet. So wrote a simple kotlin code like this to test.
abstract class Base {
val text: String
constructor(text: String) {
this.text = text
println(text + getTextSuffix())
}
abstract fun getTextSuffix(): String
}
class Derived(text: String) : Base(text) {
override fun getTextSuffix() = "_"
}
fun main(args: Array<String>) {
val d = Derived("stuff")
}
This code always prints "stuff_" which means that the overridden abstract method is available in constructor.
Can I rely on this behaviour in my app too? If not, what is the correct way to implement something like this in kotlin?
Kotlin here is no different from Java or most other OOP languages.
As long as you make it clear in the method's contract that the overriding
methods must not access any state in the subclass, you can safely call them from the base class's constructor. If a class breaks this rule, its method will be accessing uninitialized state.

Companion Objects in Kotlin Interfaces

I am trying to make a interface Parcelable, as such I need a interface like this
interface AB : Parcelable {
companion object {
val CREATOR : Parcelable.Creator<AB>
}
}
and my two classes A and B looking like
data class A (...): Parcelable{
...
companion object {
val CREATOR : Parcelable.Creator<AB> = object : Parcelable.Creator<AB> {
override fun newArray(size: Int): Array<AB?> {
return arrayOfNulls(size)
}
override fun createFromParcel(parcel: Parcel): AB {
return A(parcel)
}
}
}
I am struggling to implement such a interface in kotlin. It seems the interface class does not allow for the CREATOR
Perhaps I am taking the wrong approach,
I have a parcelable that contains a list of classes that are either A or B
so I am doing
parcel.readTypedList(this.list, AB.CREATOR)
I require that the list be either A or B and that is why I am using an interface.
Anyone have any advice or a possible solution?
In Kotlin, an interface can have a companion object but it is not part of the contract that must be implemented by classes that implement the interface. It is just an object associated to the interface that has one singleton instance. So it is a place you can store things, but doesn't mean anything to the implementation class.
You can however, have an interface that is implemented by a companion object of a class. Maybe you want something more like this:
interface Behavior {
fun makeName(): String
}
data class MyData(val data: String) {
companion object: Behavior { // interface used here
override fun makeName(): String = "Fred"
}
}
Note that the data class does not implement the interface, but its companion object does.
A companion object on an interface would be useful for storing constants or helper functions related to the interface, such as:
interface Redirector {
fun redirectView(newView: String, redirectCode: Int)
companion object {
val REDIRECT_WITH_FOCUS = 5
val REDIRECT_SILENT = 1
}
}
// which then can be accessed as:
val code = Redirector.REDIRECT_WITH_FOCUS
By convention classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creator interface.
You need to annotate CREATOR property with #JvmField annotation to expose it as a public static field in containing data class.
Also you can take a look at https://github.com/grandstaish/paperparcel — an annotation processor that automatically generates type-safe Parcelable wrappers for Kotlin and Java.

Categories

Resources