question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Data Binding click listener is included in generated model's equals and hashCode

See original GitHub issue

I’m using Epoxy version 3.3.0 with databinding. My model has a click listener which is Kotlin Function1<String, Unit>. In the documentation it mentions that DoNotHash is enabled by default for any variable whose type does not implement equals and hashcode.

According to my understanding DoNotHash should be applied to this click listener, but after inspecting the generated code, I see that that’s not the case and in fact the listener is part of the generated equals and hashCode.

Here’s a snippet of my viewholder layout:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        tools:context="org.codepond.commitbrowser.commitlist.CommitListActivity">
    <data>
        <import type="kotlin.jvm.functions.Function1"/>
        <import type="kotlin.Unit"/>

        <variable name="click" type="Function1&lt;String, Unit>"/>
        <variable name="commitInfo" type="org.codepond.commitbrowser.commitlist.CommitInfo"/>
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/commit_list_item"
        android:layout_width="match_parent"
        android:layout_height="@dimen/three_line_list_item_height"
        android:background="?selectableItemBackground"
        android:onClick="@{() -> click.invoke(commitInfo.sha)}"
        android:clickable="true"
        android:focusable="true">

And here’s a snippet of the generated model code:

  @Override
  public boolean equals(Object o) {
    if (o == this) {
      return true;
    }
    if (!(o instanceof CommitInfoBindingModel_)) {
      return false;
    }
    if (!super.equals(o)) {
      return false;
    }
    CommitInfoBindingModel_ that = (CommitInfoBindingModel_) o;
    if (((onModelBoundListener_epoxyGeneratedModel == null) != (that.onModelBoundListener_epoxyGeneratedModel == null))) {
      return false;
    }
    if (((onModelUnboundListener_epoxyGeneratedModel == null) != (that.onModelUnboundListener_epoxyGeneratedModel == null))) {
      return false;
    }
    if (((onModelVisibilityStateChangedListener_epoxyGeneratedModel == null) != (that.onModelVisibilityStateChangedListener_epoxyGeneratedModel == null))) {
      return false;
    }
    if (((onModelVisibilityChangedListener_epoxyGeneratedModel == null) != (that.onModelVisibilityChangedListener_epoxyGeneratedModel == null))) {
      return false;
    }
    if ((click != null ? !click.equals(that.click) : that.click != null)) {
      return false;
    }
    if ((commitInfo != null ? !commitInfo.equals(that.commitInfo) : that.commitInfo != null)) {
      return false;
    }
    return true;
  }

  @Override
  public int hashCode() {
    int result = super.hashCode();
    result = 31 * result + (onModelBoundListener_epoxyGeneratedModel != null ? 1 : 0);
    result = 31 * result + (onModelUnboundListener_epoxyGeneratedModel != null ? 1 : 0);
    result = 31 * result + (onModelVisibilityStateChangedListener_epoxyGeneratedModel != null ? 1 : 0);
    result = 31 * result + (onModelVisibilityChangedListener_epoxyGeneratedModel != null ? 1 : 0);
    result = 31 * result + (click != null ? click.hashCode() : 0);
    result = 31 * result + (commitInfo != null ? commitInfo.hashCode() : 0);
    return result;
  }

As you can see above, equals and hashCode include click.

Please note that I also tried with a standard interface such as

  interface OnClickListener {
    fun onClick(sha: String)
  }

which I then used in the viewholder layout XML, yet the generated code still included it in equals and hashCode.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:6 (2 by maintainers)

github_iconTop GitHub Comments

1reaction
elihartcommented, Apr 7, 2019

I was wrong earlier - the wiki is right, and this is correct -> DoNotHash is enabled by default for any variable whose type does not implement equals and hashcode.

I was on vacation for a few weeks when I replied and didn’t have a my laptop.

You can see the code that implements this at https://github.com/airbnb/epoxy/blob/7523c9ec2a4ff9daf0aada9eaab407ba2c1f737c/epoxy-processor/src/main/java/com/airbnb/epoxy/DataBindingAttributeInfo.kt#L17

I’m not sure why it is excluding Function1 - the processor code needs equals and hashcode to only exist on the base Object, so perhaps the kotlin implementation also defines those functions.

If you are interested in pursuing this can you open a new issue for Support Kotlin Function1 in Databinding DoNotHash

And if anybody wants to fix it it may be as simple as adding it to the whitelist: https://github.com/airbnb/epoxy/blob/ed916a0ea64beb7f28d18d2b1786e33aacbb1975/epoxy-processor/src/main/java/com/airbnb/epoxy/HashCodeValidator.java#L31

0reactions
Nimroddacommented, Apr 7, 2019

@elihart I see you closed the issue. I think it would be a good idea to at least update the documentation to clarify this restriction.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Layouts and binding expressions | Android Developers
Listener bindings are binding expressions that run when an event happens. They are similar to method references, but they let you run arbitrary ......
Read more >
Epoxy for Android - Airbnb Engineering & Data Science
Sometimes, you may not want certain fields to be included in your hash code and equals such as a click listener that gets...
Read more >
Epoxy — Building Complex Interfaces in RecyclerView — 9
Epoxy is called on each Model equals and hashCode to determine the current state of the Model and when the Model has changed....
Read more >
Changing Data in the ViewModel - CommonsWare
Changing Data in the ViewModel · In Java, identity equality is via == , and we have to examine each one of the...
Read more >
Boilerplate - fossasia
The Open Even App Android project contains data/model classes like Event, ... which fields to include and exclude in the toString(), equals() and...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found