5

I had this simple viewmodel provider factory code (borrowed from one of Google's code samples), which happily obliged and compiled perfectly...

fun <VM : ViewModel> viewModelProviderFactoryOf(
    create: () -> VM
): ViewModelProvider.Factory = SimpleFactory(create)


private class SimpleFactory<VM : ViewModel>(
    private val create: () -> VM
) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        val vm = create()
        if (modelClass.isInstance(vm)) {
            @Suppress("UNCHECKED_CAST")
            return vm as T
        }
        throw IllegalArgumentException("Can not create ViewModel for class: $modelClass")
    }
}

... Until i introduced this library:

implementation "androidx.navigation:navigation-compose:2.5.0-rc02"

Now all of a sudden compilation fails with:

Inheritance from an interface with '@JvmDefault' members is only allowed with -Xjvm-default option

(error pointing to : ViewModelProvider.Factory )

Why? What did navigation bring in with it? (I did confirm 100% it's the navigation lib causing it, remove it, and the error is gone)

Note: Q is not about how to solve it, compiler suggests it clearly, adding these args - freeCompilerArgs += "-Xjvm-default=all". The Q is about why this is happening.

Dmitri
  • 2,563
  • 1
  • 22
  • 30
  • Does this answer your question? [How do I resolve error message: "Inheritance from an interface with '@JvmDefault' members is only allowed with -Xjvm-default option"](https://stackoverflow.com/questions/70992947/how-do-i-resolve-error-message-inheritance-from-an-interface-with-jvmdefault) – Ryan Payne Sep 20 '22 at 19:07

4 Answers4

9

OK, found it. The problem is that bringing in compose navigation, version 2.5.0-rc also updates androidx.lifecycle to 2.5.0-rc (from, in my case, 2.3.0), and in there, they changed the Factory interface by adding a method with implementation (and also adding implementation to the existing method in the interface).

Compare:

Factory implementation before 2.5.0:

public interface Factory {
        /**
         * Creates a new instance of the given {@code Class}.
         * <p>
         *
         * @param modelClass a {@code Class} whose instance is requested
         * @param <T>        The type parameter for the ViewModel.
         * @return a newly created ViewModel
         */
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

Note one method, create, without implementation.

Here is the rewrite they did in 2.5.0

public interface Factory {
        /**
         * Creates a new instance of the given `Class`.
         *
         * Default implementation throws [UnsupportedOperationException].
         *
         * @param modelClass a `Class` whose instance is requested
         * @return a newly created ViewModel
         */
        public fun <T : ViewModel> create(modelClass: Class<T>): T {
            throw UnsupportedOperationException(
                "Factory.create(String) is unsupported.  This Factory requires " +
                    "`CreationExtras` to be passed into `create` method."
            )
        }

        /**
         * Creates a new instance of the given `Class`.
         *
         * @param modelClass a `Class` whose instance is requested
         * @param extras an additional information for this creation request
         * @return a newly created ViewModel
         */
        public fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T =
            create(modelClass)

       
          ...
        }

So that explains it, this interface is now a default implementation interface, and to inherit from it, need to add the compiler args, as suggested by the compiler (freeCompilerArgs += "-Xjvm-default=all").

Dmitri
  • 2,563
  • 1
  • 22
  • 30
3

We encountered this also when we added compose libraries. We solved it by adding the xjvm-default option to our gradle file.

android {
  kotlinOptions {
    freeCompilerArgs +=  "-Xjvm-default=all"
  }
}
Tyler2P
  • 2,324
  • 26
  • 22
  • 31
  • yes, the compiler suggests just that, the question is not how to solve it, but why this is happening – Dmitri Jun 23 '22 at 23:37
0

first, upgrade all of your fragment and activity dependencies to 1.5.0 and your lifecycle dependencies to 2.5.0. by doing so, your activities and fragments can handle the creationExtras part without requiring you to do anything. then in your factory method, replace this line:

//replace this line
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
//with this one
    override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {

and you are good to go. source: These extras are provided automatically by your Activity or Fragment when using Activity 1.5.0 and Fragment 1.5.0, respectively.

0

this code worked for me

val viewModel: ViewModel by viewModels(
        ownerProducer = { requireParentFragment() }, 
        factoryProducer = { VMFactory }
)
PavelZh
  • 29
  • 4