Some random IT boy
Some random IT boy

Reputation: 8467

Wrapping the call Context.getString with formatting arguments results in a IllegalFormatConversionException

I am trying to create a wrapper for the Context#getString(id, args) method so I write less code:

fun Context.string(@StringRes strId: Int, vararg fmtArgs: Any?) = getString(strId, fmtArgs)

When calling the function results in the following stack-trace:

2020-04-17 13:26:20.778 24143-24143/mypackage E/ERROR:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:267)
            ... 45 more
     Caused by: java.util.IllegalFormatConversionException: d != [Ljava.lang.Object;
        at java.util.Formatter$FormatSpecifier.failConversion(Formatter.java:4403)
        at java.util.Formatter$FormatSpecifier.printInteger(Formatter.java:2885)
        at java.util.Formatter$FormatSpecifier.print(Formatter.java:2839)
        at java.util.Formatter.format(Formatter.java:2524)
        at java.util.Formatter.format(Formatter.java:2459)
        at java.lang.String.format(String.java:2911)
        at android.content.res.Resources.getString(Resources.java:485)
        at android.content.Context.getString(Context.java:655)
        at mypackage.ktx.ContextKt.string(Context.kt:28)

Seen the issue, I took a look at kotlin.text to see how they perform String formatting with arguments...

/**
 * Uses this string as a format string and returns a string obtained by substituting the specified arguments,
 * using the default locale.
 */
@kotlin.internal.InlineOnly
public inline fun String.format(vararg args: Any?): String = java.lang.String.format(this, *args)

I adapted to the new call-style

fun Context.string(@StringRes strId: Int, vararg fmtArgs: Any?) = getString(strId, *fmtArgs)

…and still, it crashes with the same stack trace.

I decided to get just the String and then manually format it by using JetBrains implementation and see if there were some internals that I didn't think about:

fun Context.string(@StringRes strId: Int, vararg fmtArgs: Any?) = getString(strId).format(fmtArgs)

I see the issue is that the %d doesn't correspond to an object, but if Android can manage to have this working and kotlin format aswell with alike calls…

What's the solution in this case?

Upvotes: 2

Views: 1280

Answers (2)

Some random IT boy
Some random IT boy

Reputation: 8467

I adapted to the new call-style

fun Context.string(@StringRes strId: Int, vararg fmtArgs: Any?) = getString(strId, *fmtArgs)

This works. After inlining and de-inlining the gradle build-cache refreshed and this started working.

Upvotes: 5

Benoit Guina
Benoit Guina

Reputation: 74

First and last examples pass the array as first parameter because the * spread operator is missing and the compiler is fine with it because vararg fmtArgs: Any? is a Array<Any?>, but also a Any.
getString(strId, *fmtArgs) should have worked though.

Upvotes: 0

Related Questions