Cross references in type parameters

2019-01-12 06:57发布

问题:

For example in Java I could write:

public abstract class Element<S extends Snapshot> { ... }
public abstract class Snapshot<E extends Element> { ... }

And then, somewhere, extend this classes:

public class SnapshotImpl extends Snapshot<ElementImpl> { ... }
public class ElementImpl extends Element<SnapshotImpl> { ... }

But when I tried to implement same class hierarchy in Kotlin:

abstract class Element<S : Snapshot> 
abstract class Snapshot<E : Element>

I got following compilation errors:

Error:(6, 28) Kotlin: One type argument expected for class Snapshot<E> defined in model Error:(6, 25) Kotlin: One type argument expected for class Element<S> defined in model

Is there any way to reproduce same type parameter restrictions in Kotlin?

回答1:

Kotlin doesn't have raw types, you cannot just drop the type parameters.

One option similar to raw types is to use star projections:

abstract class Element<S : Snapshot<*>> { /* ... */ }
abstract class Snapshot<E : Element<*>> { /* ... */ }

But you won't be able to normally work with the type parameters generic members.


Another option is to introduce mutual constraints like this:

abstract class Element<E : Element<E, S>, S : Snapshot<S, E>>() { /* ... */ }
abstract class Snapshot<S : Snapshot<S, E>, E : Element<E, S>>() { /* ... */ }

With this definition, you can be sure that if you define SomeSnapshot: Snapshot<SomeSnapshot, SomeElement>, the type SomeElement is aware of SomeSnapshot, because it is constrained to be derived from Element<SomeElement, SomeSnapshot>.

Then the implementation would be:

class SomeElement : Element<SomeElement, SomeSnapshot>() { /* ... */ }
class SomeSnapshot : Snapshot<SomeSnapshot, SomeElement>() { /* ... */ }


回答2:

I recently came across this issue when designing one of the abstract layers of my app. First of the options in hotkey's answer fails to compile with "This type parameter violates the Finite Bound Restriction" (at least with newer Kotlin 1.2.71). The second one works, but can be optimized a bit. Even thought it is still bloated it makes a difference, especially when you have more type parameters. Here is the code:

abstract class Element<S : Snapshot<*, *>> { /* ... */ }
abstract class Snapshot<E : Element<S>, S : Snapshot<E, S>> { /* ... */ }


标签: kotlin