How to access static fields with reflect in kotlin? - 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"?

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
}

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.

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

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);
}

Related

Gson - deserialize or default

I have a class :
data class Stam(#SerializedName("blabla") val blabla: String = "")
I want to do gson.fromJson("{\"blabla\":null}", Stam::class.java)
However, it will fail because blabla is not nullable.
I want to make it so if gson failed to deserialize some variable, it will take the default value I give it.
How to achieve that?
I don't think it is possible with GSON, this is one of the reasons why kotlinx.serialization library was created. With this library it is fairly easy:
#Serializable
data class Stam(#SerialName("blabla") val blabla: String = "") //actually, #SerialName may be omitted if it is equal to field name
Json { coerceInputValues = true }.decodeFromString<Stam>("{\"blabla\":null}")
I wouldn't say it is not possible in Gson, but Gson is definitely not the best choice:
Gson has no mention on Kotlin, its runtime and specifics, so one is better to use a more convenient and Kotlin-aware tool. Typical questions here are: how to detect a data class (if it really matters, can be easily done in Kotlin), how to detect non-null parameters and fields in runtime, etc.
Data classes in Kotlin seem to provide a default constructor resolvable by Gson therefore Gson can invoke it (despite it can instantiate classes instances without constructors using unsafe mechanics) delegating to the "full-featured" constructor with the default arguments. The trick here is removing null-valued properties from input JSON so Gson would keep "default-argumented" fields unaffected.
I do Java but I do believe the following code can be converted easily (if you believe Gson is still a right choice):
final class StripNullTypeAdapterFactory
implements TypeAdapterFactory {
// The rule to check whether this type adapter should be applied.
// Externalizing the rule makes it much more flexible.
private final Predicate<? super TypeToken<?>> isClassSupported;
private StripNullTypeAdapterFactory(final Predicate<? super TypeToken<?>> isClassSupported) {
this.isClassSupported = isClassSupported;
}
static TypeAdapterFactory create(final Predicate<? super TypeToken<?>> isClassSupported) {
return new StripNullTypeAdapterFactory(isClassSupported);
}
#Override
#Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( !isClassSupported.test(typeToken) ) {
return null;
}
// If the type is supported by the rule, get the type "real" delegate
final TypeAdapter<T> delegate = gson.getDelegateAdapter(this, typeToken);
return new StripNullTypeAdapter<>(delegate);
}
private static final class StripNullTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegate;
private StripNullTypeAdapter(final TypeAdapter<T> delegate) {
this.delegate = delegate;
}
#Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegate.write(out, value);
}
#Override
public T read(final JsonReader in) {
// Another disadvantage in using Gson:
// the null-stripped object must be buffered into memory regardless how big it is.
// So it may generate really big memory footprints.
final JsonObject buffer = JsonParser.parseReader(in).getAsJsonObject();
// Strip null properties from the object
for ( final Iterator<Map.Entry<String, JsonElement>> i = buffer.entrySet().iterator(); i.hasNext(); ) {
final Map.Entry<String, JsonElement> property = i.next();
if ( property.getValue().isJsonNull() ) {
i.remove();
}
}
// Now there is no null values so Gson would only use properties appearing in the buffer
return delegate.fromJsonTree(buffer);
}
}
}
Test:
public final class StripNullTypeAdapterFactoryTest {
private static final Collection<Class<?>> supportedClasses = ImmutableSet.of(Stam.class);
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
// I don't know how easy detecting data classes and non-null parameters is
// but since the rule is externalized, let's just lookup it
// in the "known classes" registry
.registerTypeAdapterFactory(StripNullTypeAdapterFactory.create(typeToken -> supportedClasses.contains(typeToken.getRawType())))
.create();
#Test
public void test() {
final Stam stam = gson.fromJson("{\"blabla\":null}", Stam.class);
// The test is "green" since
Assertions.assertEquals("", stam.getBlabla());
}
}
I still think Gson is not the best choice here.

Confused about Kotlin's companion object definition

When i reach the companion object section in the ebook "Kotlin in action" it said that:
"if you need to write a function that can be called
without having a class instance but needs access to the internals of a class, you can write it as a member of an object declaration inside that class"
As my understanding this means a function of the companion object can access the method and properties of the class that contain it. But when i try to implement this i can't access the members of the class from its companion object'function:
class Normal() {
var name: String = "hallo"
companion object {
fun printName() {
println(name) // ERROR!!! unresolved reference name
}
}}
Did i misunderstood about this concept?
Method inside companion are kind of static by default(compared to Java & also this is how you achieve static kind of things in Kotlin) and you can not access normal variable from static method.
Same is happening here.
Edit:-
The definition in book is confusing, A companion object is not part of an instance of a class. You can't access members from a companion object, just like in Java you can't access members from a static method. But in case of utility classes where you just need to perform some operation you can call Static method which create a new instance of class and the perform some functions.
For example you can check answer by #user8320224, I am also quoting his code here,
class Normal {
private var name: String = "hallo"
private fun printName() {
println(name)
}
companion object {
fun factoryNormal(): Normal {
val normal = Normal()
normal.printName()
normal.name = "new name"
normal.printName()
return normal
}
}
}
Static members have access to the internals of a class, for example private members
class Normal() {
private var name: String = "hallo"
private fun printName() {
println(name)
}
companion object {
fun factoryNormal(): Normal {
val normal = Normal()
normal.printName()
normal.name = "new name"
normal.printName()
return normal
}
}}
companion object is the same as public static final class in Java. Therefore you can't access to var name.
Maybe this will help you:
class Normal() {
companion object {
#JvmStatic
var name: String = "hallo"
// This annotation will be helpful if you are calling
// this from Java, so it goes like Normal.printName();
#JvmStatic
fun printName() {
println(name)
}
}
}
Now you can use this in Kotlin:
Normal.name = "new name"
Normal.printName()
and if you want to use this in Java, then:
Normal.setName("new name");
Normal.printName();
A companion object is the same as "static" in Java. It doesn't actually have any instance of your class in it. So if your printname() method just said println("Hello again!") you could do the following:
println(Normal().name) // creates a new instance of Normal class and prints "hallo"
Normal.printname() // Does not create a new instance of Normal class but instead just prints "Hello again!" since we can access static methods without requiring an instance of a class itself.
Note that we didn't actually create a new Normal in that second line (no constructor brackets). The printname() method can be thought of as belonging to the definition of a class, rather than an example or instance of that class.
It's rather like the manual for a car; it can reference and talk about the internals of a car, but you need to have an actual car to do anything fun with the manual.
We can access the internals of an instance of the class, if we have one. So passing in an instance of the class would work:
class Normal() {
private var name: String = "hallo"
companion object {
fun printName(normal : Normal) {
println(normal.name) // Note that I made the "name" var private
}
}}
The companion object can also access anything that's within the companion object itself, so this would also work:
class Normal() {
companion object {
private var name: String = "hallo"
fun printName() {
println(name) // Note I moved the "name" var into the companion object
}
}}
And you can combine these:
class Normal() {
private var name: String = "Vilpe89"
companion object {
private var greeting: String = "Hello "
fun printName(normal : Normal) {
println("$greeting ${normal.name}!")
}
}}
Now you could call the above code like this:
Normal.printname(Normal()) // uses Normal's companion object
// method on an instance of Normal,
// and prints "Hello Vilpe89!"
This is very different to what would happen if they were separate classes:
class Normal() {
private var name: String = "Vilpe89"
}
class Greeting() {
private var greeting: String = "Hello "
fun printName(normal : Normal) {
println("$greeting ${normal.name}!") // This won't compile, because
// Greeting can't see normal.name
// if it's private.
}
}

Get companion class in companion object

Is there a way to get the javaClass of the companion class inside a companion object without knowing it's name?
I suppose I could get it by doing something like this:
open class TestClass {
companion object {
init {
val clazz = Class.forName(this::class.java.canonicalName.removeSuffix(".Companion"))
}
}
}
However, this does not work for class InheritingClass : TestClass(). It would still give me TestClass, not InheritingClass.
I was hoping for something more straightforward like this::class.companionClass.
Getting the class of the companion object of a given class will look like this:
TestClass::class.companionObject
Here's an example:
class TestClass {
companion object {
fun sayHello() = "Hello world"
}
}
If you want to get the class that contains the companion, since the latter is always an inner class of the former,
class TestClass {
companion object {
fun whichIsMyParentClass() = this::class.java.declaringClass // It'll return TestClass
}
}
And to further simplify, you'll also want to create an extension property:
import kotlin.reflect.KClass
val <T : Any> KClass<T>.companionClass get() =
if (isCompanion)
this.java.declaringClass
else
null
So, whenever you want to get the parent class of the companion object,
class TestClass {
companion object {
fun whichIsMyParentClass() = this::class.companionClass // It'll return TestClass
}
}
The companion class itself has no reference to the actual class as you can see in this bytecode
public final class TestClass$Companion {
private TestClass$Companion() { // <init> //()V
<localVar:index=0 , name=this , desc=LTestClass$Companion;, sig=null, start=L1, end=L2>
L1 {
aload0 // reference to self
invokespecial java/lang/Object <init>(()V);
return
}
L2 {
}
}
public TestClass$Companion(kotlin.jvm.internal.DefaultConstructorMarker arg0) { // <init> //(Lkotlin/jvm/internal/DefaultConstructorMarker;)V
<localVar:index=0 , name=this , desc=LTestClass$Companion;, sig=null, start=L1, end=L2>
<localVar:index=1 , name=$constructor_marker , desc=Lkotlin/jvm/internal/DefaultConstructorMarker;, sig=null, start=L1, end=L2>
L1 {
aload0 // reference to self
invokespecial TestClass$Companion <init>(()V);
return
}
L2 {
}
}
}
The reference is only the other way around (see decompiled kotlin class)
public final class TestClass {
public static final Companion companion = ...
}
So you can either do it as you just did by cutting off the .Companion part of the class name or you reference it by hard with TestClass::class.java (what is in my opinion no problem and the best solution)
If you need to print the class name, you can add simpleName, such as
this::class.java.declaringClass.simpleName

Kotlin static methods and variables

I want to be able to save a class instance to a public static variable but I can't figure out how to do this in Kotlin.
class Foo {
public static Foo instance;
public Foo() {
instance = this;
}
}
Update: since this answer is getting a decent amount of upvotes, I really wanted to say that you shouldn't do the below, but instead just use object Foo { ... }, like Roman rightly points out in the comment.
Previous answer:
The closest thing to Java's static fields is a companion object. You can find the documentation reference for them here: https://kotlinlang.org/docs/reference/object-declarations.html#companion-objects
Your code in Kotlin would look something like this:
class Foo {
companion object {
lateinit var instance: Foo
}
init {
instance = this
}
}
If you want your fields/methods to be exposed as static to Java callers, you can apply the #JvmStatic annotation:
class Foo {
companion object {
#JvmStatic lateinit var instance: Foo
}
init {
instance = this
}
}
It looks that you want to define a singleton object. It is supported in Kotlin as a first-class concept:
object Foo {
...
}
All the boilerplate code with static field and constructor is taken care by the Kotlin automatically. You don't have to write any of that.
From the Kotlin code you can refer to the instance of this object simply as Foo. From the Java code you can referer to the instance of this object as Foo.INSTANCE, because the Kotlin compiler automatically creates the corresponding static field named INSTANCE.
first you create a simple class then after create a block followed by companion object keyword
for example:
class Test{
companion object{
fun getValue(): String{
return "Test String"
}
}
}
you can call this class function using class name dot function name
for example:
// here you will get the function value
Test.getValue()
You can create a companion object for the class, and if you want the field to be static you can use the annotation #JvmStatic. Companion object have access to private members of the class it is companion for.
See below an example:
class User {
private lateinit var name: String
override fun toString() = name
companion object {
#JvmStatic
val instance by lazy {
User().apply { name = "jtonic" }
}
}
}
class CompanionTest {
#Test
fun `test companion object`() {
User.instance.toString() shouldBe "jtonic"
}
}

How to generate serialVersionUID for kotlin exceptions?

As kotlin doesn't have static fields, should I define serialVersionUID in companion object?
To create the serialVersionUID for a class in Kotlin you have a few options all involving adding a member to the companion object of the class.
The most concise bytecode comes from a private const val which will become a private static variable on the containing class, in this case MySpecialCase:
class MySpecialCase : Serializable {
companion object {
private const val serialVersionUID: Long = 123
}
}
You can also use these forms, each with a side effect of having getter/setter methods which are not necessary for serialization...
class MySpecialCase : Serializable {
companion object {
private val serialVersionUID: Long = 123
}
}
This creates the static field but also creates a getter as well getSerialVersionUID on the companion object which is unnecessary.
class MySpecialCase : Serializable {
companion object {
#JvmStatic private val serialVersionUID: Long = 123
}
}
This creates the static field but also creates a static getter as well getSerialVersionUID on the containing class MySpecialCase which is unnecessary.
But all work as a method of adding the serialVersionUID to a Serializable class.
Yes, you can declare it in the companion object. The doc says:
Also, public properties defined in objects and companion objects, as well as top-level properties annotated with const, are turned into static fields in Java
But that seems to be the case with private properties too:
class MyException: Exception() {
companion object {
private val serialVersionUid: Long = 1
}
}
javap -c -p com.ninja_squad.kotlindiscovery.MyException.class
Compiled from "MyException.kt"
public final class com.ninja_squad.kotlindiscovery.MyException extends java.lang.Exception {
private static final long serialVersionUid;
public static final com.ninja_squad.kotlindiscovery.MyException$Companion Companion;
static {};
Code:
0: getstatic #38 // Field com/ninja_squad/kotlindiscovery/MyException$Companion.INSTANCE:Lcom/ninja_squad/kotlindiscovery/MyException$Companion;
3: putstatic #40 // Field Companion:Lcom/ninja_squad/kotlindiscovery/MyException$Companion;
6: lconst_1
7: putstatic #21 // Field serialVersionUid:J
10: return
public com.ninja_squad.kotlindiscovery.MyException();
Code:
0: aload_0
1: invokespecial #15 // Method java/lang/Exception."<init>":()V
4: return
public static final long access$getSerialVersionUid$cp();
Code:
0: getstatic #21 // Field serialVersionUid:J
3: lreturn
}
You can use this plugin GenerateSerialVersionUID to automatically generate the serialVersionUID field.
Since Kotlin language does not support static, you need to use the const decoration in the generated companion object (of course, it is also possible to use the #JvmField annotation).