Почему я получаю ошибку "унаследованное конфликт платформы объявлений" в производном классе? - PullRequest
0 голосов
/ 19 сентября 2018

Имеет интерфейс (определенный из зависимого SDK и не может вносить изменения, BaseDelegate также из sdk) и базовый класс:

interface ViewDelegate {

    fun getDataType() : String
    fun getItemViewType() : Int

    fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder
    fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: Data, position: Int)

    fun getClickHandler() : View.OnClickListener
    fun setClickHandler(clickHanlder: View.OnClickListener)
}

open class BaseDelegate(@JvmField var clickHandler: View.OnClickListener) : ViewDelegate {

    @JvmField var layoutId = R.layout.base_view

    override fun getClickHandler(): View.OnClickListener {
        return clickHandler
    }

    override fun setClickHandler(clickHanlder: View.OnClickListener) {
        clickHandler = clickHanlder
    }

    override fun getDataType(): String {
        return "Base_TYPE"
    }

    override fun getItemViewType(): Int {
        return 1000
    }

    override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder {
        return BaseViewHolder(parent.inflate(layoutId, false), clickHandler)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: IData, position: Int) {
        (holder as? BaseViewHolder)?.bindView(item, position)
    }
}

Это хорошо.

Но если сделать производный класс из базового, он получит ошибку

class DerivedViewDelegate(clickHandler: View.OnClickListener) : BaseDelegate(clickHandler){

    init {
        layoutId = R.layout.child_view
    }

    override fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder {
        val vw = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false)
        return ChildViewHolder(vw, clickHandler)
    }
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, item: Data, position: Int) {
        (holder as? ChildViewHolder)?.bindView(item, position)
    }
}

Ошибка говорит:

inherited platform declaration clash: The following declarations have same JVM signature (getClickHandler()Landroid/view$OnClickListner;)  fun <get-clickHandler>(): View.OnClickListener defined in DerivedViewDelegate fun getClickHandler(): View.OnClickListener defined in DerivedViewDelegate

inherited platform declaration clash: The following declarations have same JVM signature (setClickHandler(Landroid/view$OnClickListner;)V)  fun <set-clickHandler>(<set-?>: View.OnClickListener): Unit  defined in DerivedViewDelegate fun setClickHandler(clickHandler: View.OnClickListener): Unit defined in DerivedViewDelegate

В Java это нормально:

class DerivedViewDelegate extends BaseDelegate {

    public DerivedViewDelegate(View.OnClickListener clickHandler) {
            super(clickHandler);
            layoutId = R.layout.child_view;
        }
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder holder, Data item, int position) {
            ((ChildViewHolder )holder).onBindViewHolder(item, position);
}

@NotNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent) {

    View vw = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
    return new ChildViewHolder(vw, clickHandler);

}
}

1 Ответ

0 голосов
/ 19 сентября 2018

Основная проблема становится видимой, если вы удалите @JvmField;если вы удалите его из BaseDelegate, то получите там ту же ошибку.

То, что происходит, - поле создает свои собственные методы получения и установки, которые конфликтуют с методами в родительском классе.Использование @JvmField в производном классе не работает;это все еще показывает ту же ошибку.

Итак, вместо объявления геттеров и сеттеров, существует относительно простое решение.Интерфейсы в Kotlin могут иметь поля.Как бы то ни было, они скомпилированы для получателей и установщиков из того, что я могу сказать.В любом случае, главное в том, что он позволяет легко работать с необходимыми переменными в интерфейсах.

Итак, измените ваш интерфейс следующим образом:

interface ViewDelegate {
    var clickHandler: View.OnClickListener

    fun getDataType() : String
    fun getItemViewType() : Int

    fun onCreateViewHolder(parent: ViewGroup): RecyclerView.ViewHolder
    fun onBindViewHolder(holder: RecyclerView.ViewHolder, data: ClipData.Item, position: Int)

}

Обратите внимание, что я удалил геттерыи установщики, и просто заменили его на переменную.

Теперь, в классе BaseDelegate, вы должны переопределить переменную:

class BaseDelegate(override var clickHandler: View.OnClickListener) : ViewDelegate

@ JvmField ушел.Также не забудьте удалить свой метод получения и установки в классе, они вам больше не понадобятся.После удаления @JvmField, метод получения и установки в BaseDelegate и ViewDelegate, в дополнение к добавлению поля, должен скомпилироваться.


Поскольку вы не можете изменить интерфейс (кстати, это лучший вариант, который у вас есть), есть еще один вариант.Закрытые поля не используют методы получения и установки, если они не определены явно.Возьмите этот пример:

class Test(){
    var x: Int = 0
    fun something(){
        x = 3
    }
}

Он компилируется в:

public final class Test {
   private int x;

   public final int getX() {
      return this.x;
   }

   public final void setX(int var1) {
      this.x = var1;
   }

   public final void something() {
      this.x = 3;
   }
}

Обратите внимание на прямую ссылку на x.Теперь, если я сделаю это приватным:

class Test(){
    private var x: Int = 0
    fun something(){
        x = 3
    }
}

Это произойдет:

public final class Test {
   private int x;

   public final void something() {
      this.x = 3;
   }
}

Получатель и установщик исчезли.Однако, если я явно объявлю их:

class Test(){
    private var x: Int = 0
        get() = field
        set(v) {
            field = v;
        }
    fun something(){
        x = 3
    }
}

Он выдаст:

public final class Test {
   private int x;

   private final int getX() {
      return this.x;
   }

   private final void setX(int v) {
      this.x = v;
   }

   public final void something() {
      this.setX(3);
   }
}

То же самое происходит с общедоступной переменной;если получатель объявлен и он настроен (не просто get set), он использует .setX(v) вместо this.x = v.

Как это применимо к вашей ситуации?

Вы столкнулисьобъявления сгенерированного геттера и геттера, объявленного библиотекой.Итак, поскольку приватные поля не создают геттер и / или сеттер, если они не определены явно, измените ваш var на приватный:

open class BaseDelegate(private var clickHandler: View.OnClickListener) : ViewDelegate

Обратите внимание, что это нарушает синтаксис доступа к свойству; вы не сможете звонить derivedViewDelegateInst.clickHandler, и вам нужно будет использовать геттер даже в Котлине.

...