Tom
Tom

Reputation: 1

Defining an interface as a property of an interface in kotlin and providing a concrete implemententation in interface implementation does not work

I have interface A which has interface B as a field. When I do an implementation of A, let's say concreteA, and instead of providing interface B, I provide an implementation of B, the compiler throws me an error. I don't understand why, because since there is inheritance in Kotlin, I think this should work. I tried this with data classes by the way.


interface B {
   ....
}

data class concreteB : B {
   .....
}

interface A {
   var someField: B
}

data class concreteA : A {
   //The error happens here, compiler says it's not type of overridden
   override var someField: concreteB
}

Upvotes: 0

Views: 223

Answers (2)

gidds
gidds

Reputation: 18617

The definition of A says that every implementation must have a field storing an instance of something implementing B.  This field is var, meaning that other code can change it.

In your ConcreteA, you're overriding that to restrict the field to hold only your ConcreteB.

Now, that's fine for code calling the getter, as it will always get something of type B.

But it's a problem for code calling the setter.  What if they try to set some other implementation of B?  The interface says they can, but your concrete implementation can't.  That's why the compiler won't allow your implementation.  (That's the Liskov Substitution Principle in action, as the Kotlin guys said.)

There are several approaches you could take to make it compile:

  • You could use a val instead of a var in A, and not allow anyone else to change the value.  (Simple, but probably not what you want.)

  • You could parameterise A with the type of value, giving B as an upper bound.  That would let implementations (and subinterfaces) restrict the type to ConcreteB if they wanted to.  (That's a bit more complex, but is very flexible and may be best suited to your needs.)

  • You could keep the type of the field as B, and just initialise it to your ConcreteB (as per Alexey's answer).  (However, your ConcreteB would have to allow for the possibility of other B implementations being set later on.)

Upvotes: 1

Alexey Soshin
Alexey Soshin

Reputation: 17731

The fact that you inherited something from your dad doesn't mean that you're your dad.

The usage of data classes here is a bit confusing, since your code won't compile with them.

That's the correct way for your code to work:

interface B

class ConcreteB : B

interface A {
    var someField: B
}

class ConcreteA : A {
    override var someField: B = // That's the interface
        ConcreteB()             // That's the implementation
}

Upvotes: 1

Related Questions