иметь класс, реализующий несколько Observer

Я реализовал фрагмент, который изначально реализовал android.arch.lifecycle.Observer, и я хотел начать наблюдать за некоторыми другими данными в реальном времени, но не могу. Я начал с этого:

class MyFragment : BaseFragment(), Observer<FragmentData> {
  lateinit var viewModel: MyViewModel

  override fun onActivityCreated(savedInstanceState: Bundle?) {
    viewModel.fragmentData.observe(this, this)
  }

  override fun onChanged(data: FragmentData?) {
    activity?.title = getTitleFromData(data)
  }
}

Если я обновлю класс, чтобы включить другие наблюдаемые данные, например:

class MyFragment : BaseFragment(), Observer<FragmentData>, Observer<OtherData> {
  lateinit var viewModel: MyViewModel

  override fun onActivityCreated(savedInstanceState: Bundle?) {
    viewModel.fragmentData.observe(this, this)
  }

  override fun onChanged(otherData: OtherData) {
    // update UI from otherData
  }

  override fun onChanged(data: FragmentData?) {
    activity?.title = getTitleFromData(data)
  }
}

Я получаю сообщение об ошибке:

Параметр типа T в 'Observer' имеет несовместимые значения: FragmentData, OtherData. Супертип появляется дважды.

Мне нужна помощь в понимании того, почему компилятор не может различить разницу между типами и задается вопросом о наилучшем альтернативном шаблоне.

что-то вроде этого?:

viewModel.fragmentData.observe(this, fragmentDataObserver)

private val fragmentDataObserver = Observer<Fragmentdata> {
   activity?.title = getTitleFromData(it)
}

person Jeff    schedule 07.07.2018    source источник
comment
Вы должны иметь возможность просто использовать лямбда для вашего Observer.   -  person CommonsWare    schedule 07.07.2018
comment
При изменении данных для тех двух наблюдаемых, которые я хочу разделить, выполняется значительный объем работы. Мне не нравится идея втиснуть все это в onActivityCreated()   -  person Jeff    schedule 07.07.2018
comment
Итак, пусть ваша лямбда вызывает другую функцию, которая выполняет основную часть работы. Или используйте ссылку на функцию: stackoverflow.com/a/32600222/115145. Ваша попытка реализовать интерфейс дважды с использованием отдельных дженериков также терпит неудачу в Java: public class Scrap implements Observer<String>, Observer<Boolean> приводит к ошибке синтаксиса дублирующегося класса.   -  person CommonsWare    schedule 07.07.2018


Ответы (2)


Вы можете попробовать реализовать общий интерфейс Observer следующим образом:

class MyFragment : BaseFragment(), Observer<Any> {...} 

а затем в методе onChanged используйте

override fun onChanged(any: Any?) {
    when:
    any is isOtherData -> Do OtherData things
    any is FragmentData -> Do FragmentData things
}

Второй обходной путь — создать класс-отец для OtherData и FragmentData, FatherInterface — это просто интерфейс, в теле которого ничего нет:

interface OtherData: FatherInterface{}...
interface FragmentData : FatherInterface{}...

Тогда вы можете сделать

class MyFragment : BaseFragment(), Observer<FatherInterface>{}...

override fun onChanged(fatherInterface: FatherInterface?) {
    when{
        fatherInterface is OtherData -> ...
        fatherInterface is FragmentData -> ...
    }
    // update UI from otherData
}
person Erick    schedule 08.07.2018

Компилятор не может различить типы из-за стирания типов. Когда ваша программа компилируется, практически вся общая информация удаляется из классов. Больше нет Observer<FragmentData> и Observer<OtherData>, есть только один класс Observer. Теперь, без стирания типа, было бы разумно, чтобы такие вещи были возможны.

class MyFragment : BaseFragment(), Observer<FragmentData>, Observer<OtherData> {
   override fun onChanged(otherData: OtherData) {
      // update UI from otherData
    }

    override fun onChanged(data: FragmentData?) {
      activity?.title = getTitleFromData(data)
    }
}

но как только вы применяете стирание типа, вы в основном пытаетесь расширить один и тот же класс дважды и предоставить два разных метода переопределения для одного и того же базового метода, что не разрешено. Вот почему компилятор не позволит вам это сделать. Это самое точное описание, которое я могу дать с моими ограниченными знаниями.

Что касается альтернативы, как предложено в комментариях, попробуйте использовать лямбду вместо того, чтобы пытаться сделать сам фрагмент двойным наблюдателем.

class MyFragment : BaseFragment(), Observer<OtherData> {
    lateinit var viewModel: MyViewModel

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel.fragmentData.observe(this, Observer { onChanged(it) })
    }

    override fun onChanged(otherData: OtherData?) {
        // update UI from otherData
    }

    private fun onChanged(data: FragmentData?) {
        activity?.title = getTitleFromData(data)
    }
}
person Leo Aso    schedule 07.07.2018