user6238994
user6238994

Reputation: 23

How to access static fields with reflect in kotlin?

I have this abstract class in java:

abstract class AbsApiTestCase<T> {
    T mApi;

    @Before
    public void setUp() throws Exception {
        mApi = instanceApi((Class<T>) (
                  (ParameterizedType) getClass().getGenericSuperclass())
                     .getActualTypeArguments()[0]);
    }

    static <T> T instanceApi(Class<T> clazz) throws Exception {
        return new Retrofit.Builder()
            .baseUrl(clazz.getField("BASE_URL").get(null).toString())
            .addConverterFactory(GsonConverterFactory.create(
                    new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create()))
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .client(getClient())
            .build().create(clazz);

    }
    // some code
}

And api looks like this:

public interface GithubApi {
    String BASE_URL = "https://api.github.com/";
    // some code
}

It can be used like this:

public class GithubApiTest extends AbsApiTestCase<GithubApi> {
    // some code
}

But when I convert my code to kotlin, the static field BASE_URL looks like this:

interface GithubApi {
    companion object {
        val BASE_URL = "https://api.github.com/"
    }
    // some code
}

And BASE_URL cannot be accessed like above. I found there is a @JvmField annotation but Android studio says JvmField cannot be applied to a property defined in companion object of interface.

Is there a way to access this "static field"?

Upvotes: 2

Views: 2281

Answers (4)

Aleosha
Aleosha

Reputation: 151

Wouldn't suggest putting constants in interfaces at all. It introduces too much complexity without real value gain.

Try decoupling class that holds the constant from the actual class that implements the interface.

public class AllUrls {
    public static final String GITHUB_URL = "https://api.github.com/";
}

That will become

object AllUrls {
    val GITHUB_URL = "https://api.github.com/"
}

And to use it

 static <T> T instanceApi(Class<T> clazz) throws Exception {
    return new Retrofit.Builder()
        .baseUrl(AllUrls.INSTANCE.getGITHUB_URL())
        .addConverterFactory(GsonConverterFactory.create(
                new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create()))
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .client(getClient())
        .build().create(clazz);

}

Upvotes: 1

voddan
voddan

Reputation: 33779

I see four basic options for that:

1) Extract the property into an object (companion or not):

object GithubApiUrls {
    val BASE_URL = "https://api.github.com/"
}

2) Make a package-level property:

package myProgram

val BASE_URL = "https://api.github.com/"

3) Use a class, not an interface, if you need inheritance (no idea what for, TBH):

open class GithubApi {
    val BASE_URL = "https://api.github.com/"
}

4) Replace the field with a method in the interface (can be overridden):

interface GithubApi {
    fun BaseUrl() = "https://api.github.com/"
}

Upvotes: 1

EPadronU
EPadronU

Reputation: 1813

How about making BASE_URL a compile-time constant?

interface GithubApi {
    companion object {
        const val BASE_URL = "https://api.github.com/"
    }
}

At byte-code level BASE_URL is a static field of the GithubApi interface.

public interface GithubApi {
  public static final GithubApi$Companion Companion;

  public static final java.lang.String BASE_URL;

  static {};
    Code:
       0: new           #26                 // class GithubApi$Companion
       3: dup
       4: aconst_null
       5: invokespecial #30                 // Method GithubApi$Companion."<init>":(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
       8: putstatic     #32                 // Field Companion:LGithubApi$Companion;
      11: return
}

Upvotes: 2

Rocoty
Rocoty

Reputation: 406

The @JvmStatic annotation will make the property's backing field a static one. That is, if the annotation is applied to a property within a companion object, then a new static field will be created in the enclosing class.

Note that Kotlin really has no notion of static, and that this annotation is merely for accessibility accross JVM languages.

Upvotes: 1

Related Questions