I feel that interfaces should support default valu...
# language-proposals
s
I feel that interfaces should support default values for `var`s which implementers can optionally override. I have a
FilterableListAdapter
that keeps track of what the previous search was, whether the user initiated a search or it was from the system, etc. I am going to initialize those values to false/empty string on every single implementor, and it just becomes boilerplate to do so. If the interface could define them, classes would be much cleaner and focused on what makes them unique.
y
Copy code
interface FilterableListAdapter {
    val previousSearch: String get() = ""
}
2
s
But they need to be mutable... Isn't that obvious from "previous search"?
p
if you want to define inherited mutable state, then you need an abstract class, not an interface
s
But the classes need to inherit
androidx.ListAdapter
.
p
so have your abstract class extend ListAdapter?
s
@Paul Griffith I'm sorry, I forgot that
FilterableListAdapter
implements
FilterableScreen
to add some logic specific to list adapters, but many different screens implement
FilterableScreen
, which extend many different classes (e.g.
androidx.Dialog
,
androidx.Activity
, etc.), so it needs to be an interface.
👍 1
p
Oof. Sounds like just an unfortunate corner case. I think it’s sort of impossible, at least on the JVM, to have true ‘members’ defined on an interface, at least while maintaining Java interop. Something something delegation may help? https://kotlinlang.org/docs/delegation.html
s
Why can't the Kotlin compiler simply make it a regular interface variable (mutable, as all java non-final variables are), and simply write out the initialization for me? Just automatically do what I am doing by hand anyway. Isn't that (partially) the point of a compiler? Especially the Kotlin compiler, which is largely syntactic sugar for implementing these types of things in Java.
p
Kotlin’s val/var compile down to
get/set
methods on the Java interface - there is no such thing as a true instance member variable on an interface in Java/Kotlin: https://stackoverflow.com/questions/2430756/why-are-interface-variables-static-and-final-by-default
s
@Youssef Shoaib [MOD] any thoughts? @Paul Griffith If I can do this by hand, I don't see why the compiler can't do it for me.
y
Using Paul's idea of delegation, you may do something like this (Note that this creates an extra object) (playground):
Copy code
import kotlin.time.*
interface FilterableListAdapter {
    var previousSearch: String
    var lastSearchTime: Duration
    var wasSearchInitiatedByUser: Boolean
    var otherValue: Int
    var evenMoreValues: List<Any>?
}

class FilterableListAdapterFields : FilterableListAdapter {
    override var previousSearch: String = ""
    override var lastSearchTime: Duration = 5.minutes
    override var wasSearchInitiatedByUser: Boolean = false
    override var otherValue: Int = 42
    override var evenMoreValues: List<Any>? = null
}


open class Baz
class FooBar: Baz(), FilterableListAdapter by FilterableListAdapterFields() {
    fun myCustomFunctionality() {
        println(previousSearch)
        previousSearch = "hello"
    }
}

class MapImplementationOfFilterableListAdapterForSomeReason: HashMap<Any, Any>(), FilterableListAdapter by FilterableListAdapterFields() {
    override var evenMoreValues: List<Any>? = listOf() // if you override any of the behavior on your own, you'll sadly need to initialize the property
    	get() = field!! + this.values
}

fun main() {
    val fooBar = FooBar()
    fooBar.myCustomFunctionality()
    fooBar.myCustomFunctionality()
}
TL;DR: create a class that implements the default values for the interface properties, then for each implementor, you can simply delegate to an instance of that class. It does have overhead since you're carrying around an extra object, but we're discussing conciseness here and not performance
s
Wow, that is really cool! I didn't know you can do something like that! When you say, "if you override any of the behavior on your own, you'll sadly need to initialize the property", you mean if I override any of the functions I will have to initialize the properties, or I will just have to initialize the properties that I override?
y
You'll have to initialize the ones you override, but that's it, which, I'd say, is a pretty reasonable expectation. Another downside though is that the implementors aren't automatically forced to override all the functions of the interface (because they're all implemented automatically by the
*Fields
class. A way around that is to split the interface into 2: 1 is the parent that just has the fields that will have defaults, and then 1 which has all the methods that a class needs to implement. Then your
*Fields
class will implement the parent interface, while each child will implement the interface with all the required methods. Finally, your implementors will delegate the fields to the
*Fields
class, and will also implement the "real" interface. E.g.:
Copy code
interface FilterableListAdapterDef {
    var previousSearch: String
    var lastSearchTime: Duration
    var wasSearchInitiatedByUser: Boolean
    var otherValue: Int
    var evenMoreValues: List<Any>?
}
interface FilterableListAdapter: FilterableListAdapterDef {
    fun methodTheImplementorsHaveToImplement()
}

class FilterableListAdapterFields : FilterableListAdapterDef {
    override var previousSearch: String = ""
    override var lastSearchTime: Duration = 5.minutes
    override var wasSearchInitiatedByUser: Boolean = false
    override var otherValue: Int = 42
    override var evenMoreValues: List<Any>? = null
}


open class Baz
// Error: Class 'FooBar' is not abstract and does not implement abstract member public abstract fun methodTheImplementorsHaveToImplement(): Unit defined in FilterableListAdapter
class FooBar: Baz(), FilterableListAdapterDef by FilterableListAdapterFields(), FilterableListAdapter {
    fun myCustomFunctionality() {
        println(previousSearch)
        previousSearch = "hello"
    }
}
It might be a bit too much boilerplate though for you. If the people writing the implementors are aware that they need to implement the methods even without their IDE screaming at them, then you should be fine. Otherwise, the workaround above might be needed to bring back the screaming IDE.
e
That actually makes sense to me on the first read. Can you, please, create an issue in https://kotl.in/issue with this use case.
s
Made an issue here .
@elizarov doesn't look like there is any activity.
e
Thanks. I've added some of my notes there. Let's gather use-cases for this kind of feature in that YT issue.