https://kotlinlang.org logo
#getting-started
Title
# getting-started
d

David Kubecka

01/18/2023, 2:22 PM
In subclasses, one often needs to explicitly cast a supertype to the type required by the current context, e.g.
Copy code
class Cat : Animal {
  override var owner: CatOwner

  override fun switchOwner(owner: AnimalOwner) {
    owner = owner as CatOwner
  }
}
The cast to
CatOwner
is semantically redundant because it's quite clear what the resulting type should be, only the compiler can't/shouldn't guess it. Still, these casts soon seem like a boilerplate (especially with more complex generic types) so I devised the following helper function:
Copy code
inline fun <reified TOut : T, T : Any> T.asTOut() = this as TOut
The usage would then simply be
Copy code
override fun switchOwner(owner: AnimalOwner) {
    owner = owner.asTOut()
  }
This basically emulates what the compiler does when inferring generic parameter types. Of course, the difference in my example is that it's generally unsafe (that's why the compiler doesn't do it automatically). What do you think about this? Neat or horrible?
s

Sam

01/18/2023, 2:27 PM
The cast is inherently unsafe, so I don’t like the idea of hiding it inside a helper method
I think I would rather invest the time in finding a design that avoids the unsafe cast in the first place
k

Klitos Kyriacou

01/18/2023, 2:51 PM
Perhaps we could do with covariant function parameter types? (For example, Eiffel has them.)
s

Sam

01/18/2023, 2:53 PM
@Klitos Kyriacou that sounds interesting, can you give an example? I struggle to see how it would work with polymorphism
d

David Kubecka

01/18/2023, 3:02 PM
Interesting indeed. As stated in the quoted section, the Eiffel approach isn't typesafe. OTOH the problem is real - it's IMO not a coincidence that I face that exact same issue in my production app at several places. It seems that neither solution is ideal. With Eiffel, the code is inherently type unsafe, but you don't need any casting. With Kotlin (and most other statically typed languages), it's the opposite. Not sure what is better though...
e

ephemient

01/18/2023, 3:05 PM
Copy code
interface Animal<A : Animal<A, O>, O : Owner<O, A>> {
    var owner: O
}

interface Owner<O : Owner<O, A>, A : Animal<A, O>>

class Cat : Animal<Cat, CatOwner> {
    override var owner = CatOwner()
}

class CatOwner : Owner<CatOwner, Cat>
may be overkill depending on the use case, but this forbids
Cat().owner = notCatOwner()
at compile time instead of a runtime cast
d

David Kubecka

01/18/2023, 3:07 PM
Yeah, I tried that at first but that is a rabbit hole once you have more aspects which translate to additional generic parameters in this design.
Basically, I decided that a few "internal" casts constitute a more pragmatic approach than the clean solution with generics where the types quickly become repetitive and unreadable.
10 Views