Reputation: 23
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
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
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
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
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