How to NOT pass arguments to parameters with default values in Data Binding

Viewed 1529

Is it possible to call Kotlin function with default parametrs from XML without passing arguments

This is my ViewModel:

class MyViewModel: ViewModel(){

    fun doSomething(myVar: String = "defValue"){

    }
}

XML:

....
<data>
    <variable
        name="viewModel"
        type="com.example.MyViewModel" />
</data>
....
android:onClick="@{(view) -> viewModel.doSomething()}"  --> ERROR
android:onClick="@{(view) -> viewModel.doSomething(`SomeString`)}"  --> WORKING
....

When I call the doSomething method without argument from XML with Data Binding I am getting error.

[databinding] {"msg":"cannot find method doSomething() in class com.example.MyViewModel","file":"app\\src\\main\\res\\layout\\fragment_list.xml","pos":[{"line0":56,"col0":41,"line1":56,"col1":65}]}
2 Answers

Great question @DeveScie

The reason it failed is because the doSomething() without any parameter function does not exists in byte code level. To understand, you need to see how your kotlin code is converted into Java byte code.

Here is your class's Java representation (some unrelated code is omitted)

public final class MyViewModel {
   public final void doSomething(@NotNull String myVar) {
      Intrinsics.checkParameterIsNotNull(myVar, "myVar");
   }

   // $FF: synthetic method
   public static void doSomething$default(MyViewModel var0, String var1, int var2, Object var3) {
      if ((var2 & 1) != 0) {
         var1 = "defValue";
      }

      var0.doSomething(var1);
   }
}

And here is a Kotlin test class that uses this function

class TestClass {
    fun testFunction() {
        val vm = MyViewModel()
        vm.doSomething()
        vm.doSomething("Value")
    }
}

Which results in Java code below

public final class TestClass {
   public final void testFunction() {
      MyViewModel vm = new MyViewModel();
      MyViewModel.doSomething$default(vm, (String)null, 1, (Object)null);
      vm.doSomething("Value");
   }
}

As you can see, the default value you have in kotlin is actually called by:

MyViewModel.doSomething$default(vm, (String)null, 1, (Object)null);

So, you should have a separate method for other case. Hope it helps.


UPDATE: Based on @Uli's answer below, another solution is to add @JvmOverloads to your kotlin function.

class MyViewModel {
    @JvmOverloads
    fun doSomething(myVar: String = "defValue") {
    }
}

Now becomes following in Java

public final class MyViewModel {
    @JvmOverloads
    public final void doSomething() {
        doSomething$default(this, (String) null, 1, (Object) null);
    }

    @JvmOverloads
    public final void doSomething(@NotNull String myVar) {
        Intrinsics.checkParameterIsNotNull(myVar, "myVar");
    }

    // $FF: synthetic method
    public static void doSomething$default(MyViewModel var0, String var1, int var2, Object var3) {
        if ((var2 & 1) != 0) {
            var1 = "defValue";
        }

        var0.doSomething(var1);
    }
}

Thanks Uli for reminding me about this option.

Related