Home > Mobile >  Implementing ViewModelProvider.Factory fails with Inheritance from an interface with '@JvmDefau
Implementing ViewModelProvider.Factory fails with Inheritance from an interface with '@JvmDefau

Time:06-25

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.

CodePudding user response:

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"
    }
}

CodePudding user response:

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").

  • Related