Hi! How can I augment a generic interface dependin...
# getting-started
j
Hi! How can I augment a generic interface depending on the generic type? I would like to do something like this:
Copy code
interface MyInterface<T> {
    val field1: Any
}

interface MyInterface<T: CharSequence>: MyInterface<Any> {
    val field2: Any
}
Which could be used this way:
Copy code
fun test(value1: MyInterface<Int>, value2: MyInterface<String>) {
    value1.field1 // should work
    value1.field2 // should NOT work, because it matches `MyInterface<T>`
    value2.field1 // should work
    value2.field2 // should work, because it matches `MyInterface<T: CharSequence>`
}
But I have two issues: •
MyInterface<T: CharSequence>
inheriting
MyInterface<Any>
doesn’t seem to work due to a cyclic dependency •
value2: MyInterface<String>
doesn’t resolve to
MyInterface<T: CharSequence>
, like I would have naively expected, but to
MyInterface<T>
, which doesn’t allow to use
field2
. Is it possible or am I going down the wrong path here?
e
no, it's not possible'
j
Why not give 2 different names to those types?
e
if it were stateless you can limit an extension:
Copy code
fun <T : CharSequence> MyInterface<T>.get2(): Any
but not something that requires a state
an extension property with only getter (and possibly setter) is also possible if there's no backing field, but is not possible if there is
Joffrey's suggestion is something like
Copy code
interface MySubinterface<T : CharSequence> : MyInterface<T> {
    val field2: Any
}
j
@Joffrey I wanted to create a function able to accept a type and easily return the same type decorated, something like that:
Copy code
fun <T> wrapIt(): MyInterface<T>
Using generics I could avoid a switch in the function implementation to map a specific type to a specific interface/class to decorate it.
But I guess I have to go with the switch 😄
Thank you!
j
Well, you cannot avoid the switch if you need a special implementation that provides the value for
field2
. If you don't need anything special, e.g. if
field2
can be computed from other state, then the extension approach suggested by @ephemient should do what you need
j
Good to know, I will evaluate the possibilities between the switch and the extensions. Thank you 😊
m
The cyclic dependency issue arises because you're trying to inherit from
MyInterface<Any>
, which is not type-safe. Instead, you can introduce another type parameter for the supertype of
MyInterface<T>
, like this: interface MyInterface<S, T> { val field1: S } interface MyInterface<T: CharSequence>: MyInterface<Any, T> { val field2: Any } Here,
MyInterface<S, T>
has two type parameters,
S
for the supertype of
field1
and
T
for the type parameter of the interface.
MyInterface<T: CharSequence>
inherits from
MyInterface<Any, T>
, which means that
field1
has a supertype of
Any
and
field2
is added as a new property. With this change, you can use the interfaces as you intended: fun test(value1: MyInterface<Any, Int>, value2: MyInterface<Any, String>) { value1.field1 // works value1.field2 // does not compile, because it is not defined in MyInterface<Any, Int> value2.field1 // works value2.field2 // works, because it is defined in MyInterface<Any, String> } Note that you need to use
MyInterface<Any, Int>
and
MyInterface<Any, String>
as the types of the arguments to
test
. This is because
MyInterface<T: CharSequence>
only adds the
field2
property, but
field1
is still present and needs to be accessed through the supertype.
j
The problem is not a cyclic dependency, you just cannot declare twice the same type. Your example doesn't compile either: even with a different number of type parameters, you can't declare
MyInterface
twice