noktigula
noktigula

Reputation: 445

Kotlin for android and static final inner classes

I'm trying to rewrite my android app on Kotlin, and I've faces problem while converting ContentProvider contract class. So I have simple contract class (default stuff like content type and content uris is omitted):

public final class Contract {
    public static final class Contacts {
        public static final String NAME = "Name"
        public static final String BIRTH = "Birth"
        public static final String IMAGE = "Image"
    }
}

If I understand correctly, in Kotlin we don't have static members. Instead of this, we have "companion objects". So after converting it to Kotlin, I have this code:

object BirthdayContract {
    class Contacts : BaseColumns {
        companion object {
            val NAME = "Name"
            val BIRTH = "Birth"
            val IMAGE = "Image"
        }
    }
}

But when I'm trying to access fields like Contract.Contacts.NAME, I have error: "NAME has private access". Changing the visibility modifiers gave no effect. So is there some way to use such contract classes in Kotlin, or it better to keep it in Java?

UPD I think that I should clarify - my caller code is also in Kotlin. The problem was in @JvmField annotation. With it, I can access static members directly, without calling getters.

Upvotes: 6

Views: 6253

Answers (4)

AndroidEx
AndroidEx

Reputation: 15824

NAME is a property and by default its backing field has private access. But since it's a property, there's a getter for it: getNAME().

Since this is not how you naturally access constants in Java, there are a couple ways to directly expose the field:

  1. const: const val NAME = "Name"
  2. @JvmField annotation: @JvmField val NAME = "Name"

Upvotes: 6

Ingo Kegel
Ingo Kegel

Reputation: 47975

To expose constants as fields, annotate them with @JvmField

class Contacts  {
    companion object {
        @JvmField val NAME = "Name"
        @JvmField val BIRTH = "Birth"
        @JvmField val IMAGE = "Image"
    }
}

Then you can use them from Java like this:

String name = Contacts.NAME;

Once all of your code is in Kotlin, you can remove the @JvmField annotation.

Upvotes: 2

Ostkontentitan
Ostkontentitan

Reputation: 7010

Consider using plain object as this.:

object Contract {
    object Contacts {
        val NAME = "Name"
        val BIRTH = "Birth"
        val IMAGE = "Image"
    }
}

This should perfectly fill in for your initial Java-Code.

In the most recent stable Kotlin-Version public is the default visibility.

Optionially you can add the const key-word for compile time constants so that they are usable in annotations.: https://kotlinlang.org/docs/reference/properties.html#compile-time-constants

@JvmStatic annotation is for Java-Interop and if you plan to go full kotlin you won't need it.

Upvotes: 0

алекс кей
алекс кей

Reputation: 831

You can do it with a @JvmStatic

object BirthdayContract {
    class Contacts : BaseColumns {
        companion object {
            @JvmStatic val NAME = "Name"
            @JvmStatic val BIRTH = "Birth"
            @JvmStatic val IMAGE = "Image"
        }
    }
}

//Java
BirthdayContract.Contacts.Companion.getNAME()

So far I see no good reason to extend BaseColumns, so a better choice whould be

object BirthdayContract {
    object Contacts  {
           const val NAME = "Name"
           const val BIRTH = "Birth"
           const val IMAGE = "Image"

    }
}
//Java
BirthdayContract.Contacts.NAME

The bottom line is that nested objects in kotlin do not look clean. Please consider simplifying your code

Upvotes: 0

Related Questions