2

Is it possible to upcast properties getter type?

For example, inside class declaration I have val member = Implementation(), but I want for the public getter to return a reference typed with SomeInterface assuming that Implementation is a subtype of SomeInterface, whereas inside the class declaration this type will be used as a Implementation instance.

Here is a complete example in Java to give a clear picture:

public class MyViewModel extends ViewModel {
    private final MutableLiveData<Settings> settings = new MutableLiveData<>();

    public LiveData<Settings> getSettings() {
        return settings;
    }

    public void updateSettings() {
        settings.setValue(new Settings());
    }
}

Here this "property" is visible outside as a LiveData (supertype of MutableLiveData), but inside the class it is possible to use it as MutableLiveData.

I would say it sounds natural, but seems that Kotlin doesn't allow this. Am I missing something? Or creating private val _private = Implementation() and val _public: SomeInterface = _private or implementing a custom method with getter semantics and modified name having SomeInterface return type is the only way to achieve this?

nyarian
  • 4,085
  • 1
  • 19
  • 51
  • Inside the class you are not working with the property. You are using field. I mean, you are not calling `getSettings().setValue(...)` which will not compile IMO, but you are calling `settings.setValue(...)` you can do this, because `settings` is of type `MutableLiveData` whereas `getSettings()` is not. I have the same use-case as you with `LiveData` and I am using the combination of `_private` and `public` variables, as you suggest in your post. – Maroš Šeleng Nov 28 '18 at 12:09
  • 1
    Sure, I'm just talking about convenience if I could, for example, write something like `val someObject = Implementation()` and `get(): SomeInterface` – nyarian Nov 28 '18 at 12:25

1 Answers1

2

I think (if I've understood your question correctly) that explicitly declaring the type of member will do what you want, e.g.

interface SomeInterface

class Implementation : SomeInterface

class Test {
    val member: SomeInterface = Implementation()
}

UPDATE: after question updated to clarify that in the Test class member should be of type Implementation, not SomeInterface...

I don't think what you're trying to do can be done without having a member variable, as you suggest in your question. However, if you have a base class which defines what your member should be, you can kind of do what you want:

interface SomeInterface

class Implementation : SomeInterface

abstract class Super {
    abstract val member: SomeInterface
}

class Test : Super() {
    override val member = Implementation()  
    // declared as Implementation, but does correctly implement superclass which wants this to be a SomeInterface.
    // In this class, member is of type Implementation.
}

fun test() {
    val test1 = Test()
    val member1 = test1.member  // member1 is an Implementation object

    val test2: Super = Test()
    val member2 = test2.member  // member2 is a SomeInterface object
}

What's shown above is that you can have a member which is available in your class as an Implementation, but is visible outside the class as a SomeInterface. But only if, when you're working with it outside the class, you're working with it as an instance of the superclass which defines member as being of type SomeInterface.

Yoni Gibbs
  • 6,518
  • 2
  • 24
  • 37
  • Sorry, I've updated the question: it is not what I want, because inside the `Test` class I cannot treat `member` as a `Implementation` type – nyarian Nov 28 '18 at 11:53
  • It will sure work with early binding and covariance trick, but the question is in upcasting without inheritance and abstract method declaration. But anyway, you have confirmed that it is impossible. Thank you! – nyarian Nov 28 '18 at 12:15