Let's say I have an interface Base and that we implement that interface in class Base1 : Base.
I would expect an extension function in the form
fun ArrayList<Base>.myFun()
to also work on arrayListOf(Base1(), Base1()).myFun(), but it doesn't. It requires the list to be of type Base instead of Base1.
Isn't this really strange? Or am I just missing something?
And, what are my options to write a function available on all subclasses of an interface?
Thanks!
You need to allow extension function to accept child implementation
interface Base
class Base1: Base
fun ArrayList<out Base>.myFun() = println(toString())
fun main(args: Array<String>) {
arrayListOf(Base1(), Base1()).myFun()
}
Lets say that you have:
interface Base
class Base1: Base
class Base2: Base
To have a fun for each class that extends your interface, you should create a class with a generic extension:
fun <T : Base> ArrayList<T>.myFun() = arrayListOf<T>()
In this case what you are doing is do an extension on any ArrayList which container class would be any class that extends from your Base interface. In the same way it will return an arraylist of that type.
In code this will look like:
fun myCode(){
val array1 : ArrayList<Base1> = arrayListOf(Base1()).myFun()
val array2 : ArrayList<Base2> = arrayListOf(Base2()).myFun()
}
You can read more about it in the official documentation, it has a lot of generic examples too: https://kotlinlang.org/docs/reference/extensions.html
This:
fun ArrayList<Base>.myFun()
is an extension only on ArrayList<Base>.
You can make the function generic to support any implementers of Base:
fun <T : Base> ArrayList<T>.myFunc() {}
Usage:
ArrayList<Base1>().myFunc()
This should work with array list of any type:
fun <T> ArrayList<T>.myFun()
Related
I am trying to pass a value (class) to another class, and in that class I will use the parameter(the passed class) with enumValues<parameter>()
I have tried passing generic MyClass<T>()
and than enumValues<T>()
but I am getting "cannot use T as a reified , use class instead".
Depending on the context of where you're using this, you can simply declare the method as inline and reified:
inline fun <reified T : Enum<T>> useEnumValues() {
val values = enumValues<T>()
}
Otherwise, if you require the class reference:
fun <T : Enum<T>> useEnumSet(enumClass: Class<T>) {
val values = EnumSet.allOf(enumClass)
}
If your Class value is called cls, just call cls.enumConstants.
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.
I use this code to add extension for Log class android
fun Log.i2(msg:String):Unit{
Log.i("Test",msg)
}
when using in the activity
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
Log.i2("activity_main")
}
}
Log.i2 not found. What's wrong?
To achieve extension function in static class, you need to write extension of the companion object(refer this)
fun Log.Companion.i2(msg:String) {
...
}
You have created Extension function of Class Log.
Which is suppose to call by Instance of Class Log. You are trying to treat extension function as static and calling it by Class name. Which is not correct in the case
Currently, static extension methods in Kotlin is not supported without the companion object, because android.util.Log is a java class, so there is no companion object.
Instead, I recommend you to use a static function (package-level function, simply declared outside a class in a source code file):
fun logI2(msg: String) {
Log.i("Test", msg)
}
And just use it like this:
logI2("activity_main")
I'm trying to create a RecyclerView.Adapter with the following:
View holders - Provided the abstract parent and one son
abstract class BaseSettingsViewHolder<T>(var viewDataBinding :
ViewDataBinding) : RecyclerView.ViewHolder(viewDataBinding.root) {
abstract fun onBind(data: T, presenter: ISettingsPresenter, position: Int)
}
class SettingsTitleViewHolder(viewDataBinding: ViewDataBinding) : BaseSettingsViewHolder<TitleData>(viewDataBinding) {
override fun onBind(data: TitleData, presenter: ISettingsPresenter, position: Int) {
viewDataBinding.setVariable(BR.titleData, data)
viewDataBinding.setVariable(BR.itemPosition, position)
viewDataBinding.setVariable(BR.settingsPresenter, presenter)
viewDataBinding.executePendingBindings()
}
}
And when trying to declare the adapter:
class SettingsAdapter(var context: Context, var presenter: ISettingsPresenter) : RecyclerView.Adapter<BaseSettingsViewHolder>() {
I'm getting "One type argument expected for class" compile error on the:
RecyclerView.Adapter<BaseSettingsViewHolder>
Appreciate the help!
1- If you use always SettingTitleViewHolder;
RecyclerView.Adapter<SettingTitleViewHolder>
2- If you want to use any class which extended BaseSettingViewHolder;
RecyclerView.Adapter<BaseSettingsViewHolder<*>>
3- Use Any Object Type
RecyclerView.Adapter<BaseSettingsViewHolder<Any>>
4- Use extended type parameter for adapter class
class SettingsMenuAdapter<T>:RecyclerView.Adapter<T> where T : BaseSettingViewHolder<*>
5- Define out for abstract class type parameter.
abstract class BaseSettingsViewHolder<out T> RecyclerView.Adapter<BaseSettingsViewHolder<Any>>
7- Read that article =>
"Generics in Kotlin" ;)
You should specify the type argument for the BaseSettingsViewHolder in the RecyclerView.Adapter<BaseSettingsViewHolder> type.
Kotlin, unlike Java, does not have the raw types, so you cannot simply omit the type arguments.
The closest concept to raw types is star-projected types: use BaseSettingsViewHolder<*> in RecyclerView.Adapter<BaseSettingsViewHolder<*>>, this will mean that the type argument for BaseSettingsViewHolder is unknown.
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.