Можно ли изменить тип получателя свойств Kotlin?

Можно ли повысить тип геттера свойств?

Например, внутри объявления класса у меня есть val member = Implementation(), но я хочу, чтобы геттер public возвращал ссылку, набранную с помощью SomeInterface, предполагая, что Implementation является подтипом SomeInterface, тогда как внутри объявления класса этот тип будет использоваться как экземпляр Implementation.

Вот полный пример на Java, чтобы дать четкое представление:

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());
    }
}

Здесь это «свойство» видно снаружи как LiveData (супертип MutableLiveData), но внутри класса его можно использовать как MutableLiveData.

Я бы сказал, что это звучит естественно, но кажется, что Котлин этого не позволяет. Я что-то упускаю? Или создание private val _private = Implementation() и val _public: SomeInterface = _private или реализация пользовательского метода с семантикой геттера и измененным именем, имеющим возвращаемый тип SomeInterface, является единственным способом добиться этого?


person nyarian    schedule 28.11.2018    source источник
comment
Внутри класса вы не работаете со свойством. Вы используете поле. Я имею в виду, что вы не вызываете getSettings().setValue(...), который не будет компилировать IMO, но вы вызываете settings.setValue(...), вы можете это сделать, потому что settings имеет тип MutableLiveData, а getSettings() — нет. У меня тот же вариант использования, что и у вас, с LiveData, и я использую комбинацию переменных _private и public, как вы предлагаете в своем посте.   -  person Maroš Šeleng    schedule 28.11.2018
comment
Конечно, я просто говорю об удобстве, если бы я мог, например, написать что-то вроде val someObject = Implementation() и get(): SomeInterface   -  person nyarian    schedule 28.11.2018


Ответы (1)


Я думаю (если я правильно понял ваш вопрос), что явное объявление типа member будет делать то, что вы хотите, например.

interface SomeInterface

class Implementation : SomeInterface

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

ОБНОВЛЕНИЕ: после того, как вопрос обновлен, чтобы уточнить, что в тестовом классе member должен быть тип Implementation, а не SomeInterface...

Я не думаю, что то, что вы пытаетесь сделать, можно сделать без переменной-члена, как вы предлагаете в своем вопросе. Однако, если у вас есть базовый класс, который определяет, каким должно быть ваше member, вы можете делать то, что хотите:

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
}

Выше показано, что у вас может быть member, который доступен в вашем классе как Implementation, но виден вне класса как SomeInterface. Но только если, когда вы работаете с ним вне класса, вы работаете с ним как с экземпляром суперкласса, который определяет member как относящийся к типу SomeInterface.

person Yoni Gibbs    schedule 28.11.2018
comment
Извините, я обновил вопрос: это не то, что я хочу, потому что внутри класса Test я не могу рассматривать member как тип Implementation - person nyarian; 28.11.2018
comment
Это наверняка сработает с ранним связыванием и трюком с ковариацией, но вопрос в восходящем приведении без наследования и объявления абстрактного метода. Но в любом случае, вы подтвердили, что это невозможно. Благодарю вас! - person nyarian; 28.11.2018