37

I develop Android applications and often use annotations as compile time parameter checks, mostly android's support annotations.

Example in java code:

public class Test
{
    @IntDef({Speed.SLOW,Speed.NORMAL,Speed.FAST})
    public @interface Speed
    {
         public static final int SLOW = 0;
         public static final int NORMAL = 1;
         public static final int FAST = 2;
    }

    @Speed
    private int speed;

    public void setSpeed(@Speed int speed)
    {
        this.speed = speed;
    }
}

I don't want to use enums because of their performance issues in Android. The automatic converter to kotlin just generates invalid code. How do I use the @IntDef annotation in kotlin?

Ronald Martin
  • 4,278
  • 3
  • 27
  • 32
curioushikhov
  • 2,461
  • 2
  • 30
  • 44
  • 1
    You can use enums freely, they are not 'slow' in any sense (http://stackoverflow.com/questions/5143256/why-was-avoid-enums-where-you-only-need-ints-removed-from-androids-performanc) – Alexander Udalov Mar 14 '16 at 08:22
  • @AlexanderUdalov, 1) https://www.youtube.com/watch?v=Hzs6OBcvNQE 2) enums are not parcelable by default which also adds boilerplate code when it needed to pass to intent params 3) when value received from network the way to convert it to enum is obscure and eliminates enum safety. – curioushikhov Mar 14 '16 at 10:15
  • 1
    @curioushikhov ProGuard "simplifies enum types to integer constants, whenever possible" ([Optimizations](http://proguard.sourceforge.net/manual/optimizations.html)). – mfulton26 Mar 14 '16 at 13:41
  • 1
    @mfulton26 it is true only for the simplest case, when you need to implement parcelable all the proguard magic is gone. – curioushikhov Mar 14 '16 at 14:33
  • It is not working as refer to in https://stackoverflow.com/questions/37833395/kotlin-annotation-intdef – Elye Oct 17 '18 at 01:49
  • Enums are serializable though – milosmns Aug 16 '19 at 08:27

4 Answers4

39

Edit: In case you miss the comments on the question or this answer, it's worth noting that the following technique compiles, but does not create the compile-time validation you would get in Java (which partially defeats the purpose of doing this). Consider using an enum class instead.


It is actually possible to use the @IntDef support annotation by defining your values outside of the annotation class as const vals.

Using your example:

import android.support.annotation.IntDef

public class Test {

    companion object {

         @IntDef(SLOW, NORMAL, FAST)
         @Retention(AnnotationRetention.SOURCE)
         annotation class Speed

         const val SLOW = 0L
         const val NORMAL = 1L
         const val FAST = 2L
    }

    @Speed
    private lateinit var speed: Long

    public fun setSpeed(@Speed speed: Long) {
        this.speed = speed
    }
}

Note that at this point the compiler seems to require the Long type for the @IntDef annotation instead of actual Ints.

Ronald Martin
  • 4,278
  • 3
  • 27
  • 32
  • 28
    I have done this, but Kotlin doesn't seem to enforce this constraint at all. I can provide any int/long where `@Speed` is required, and it doesn't care. – AutonomousApps Oct 11 '17 at 23:41
  • 2
    Btw, it requires a `Long` because `@IntDef` is defined as taking an array of `long` -- `long[] value() default {};` – AutonomousApps Oct 11 '17 at 23:41
  • 1
    It's correct in the sense that this is the right way to do the same thing in Kotlin. The code compiles and the compiler is aware of all annotations. But also, the IDE or compiler don't throw an error on this, so that's a separate issue :) – milosmns Aug 16 '19 at 08:29
12

There's currently no way to achieve exactly this in Kotlin, since an annotation class cannot have a body and thus you cannot declare a constant in it which would be processed by IntDef. I've created an issue in the tracker: https://youtrack.jetbrains.com/issue/KT-11392

For your problem though, I recommend you use a simple enum.

Alexander Udalov
  • 31,429
  • 6
  • 80
  • 66
5

Update:

Forget @IntDef and @StringDef, Now, with ART, you can use enums instead.

From the official GoogleIO:

https://www.youtube.com/watch?v=IrMw7MEgADk&feature=youtu.be&t=857

Plus, if you're still not sure if you should use enums, you can hear a bunch of people yelling at each other in the comments of the first answer over here: https://stackoverflow.com/a/37839539/4036390


Old answer:

Just create the @IntDef class as a java class and access it via kotlin code.

Example:

  1. Create your type class:

    public class mType { @IntDef({typeImpl.type1, typeImpl.type2, typeImpl.type3}) @Retention(RetentionPolicy.SOURCE) public @interface typeImpl { int type1 = 0; int type2 = 1; int type3 = 2; } }

  2. Put this function in any Kotlin object:

    object MyObject{ fun accessType(@mType.typeImpl mType: Int) { ... } }

  3. then access it:

    fun somOtherFunc(){ MyObject.accessType(type1) }

**Notice: you don't have to put the access method inside an object.

Oz Shabat
  • 1,434
  • 17
  • 16
2

Use this:

companion object {
    const val FLAG_PAGE_PROCESS = 0L//待处理
    const val FLAG_PAGE_EXCEPTION = 1L//设备异常
    const val FLAG_PAGE_UNCHECKED = 2L//未审核
    const val FLAG_PAGE_AUDIT = 3L//统计
    val FLAG_PAGE = "FLAG_PAGE"

    fun newInstance(@FlagPageDef flagPage: Int): RepairFormsListFragment {
        val fragment = RepairFormsListFragment()
        val args = Bundle()
        fragment.arguments = args
        return fragment
    }

    @Retention(AnnotationRetention.SOURCE)
    @IntDef(FLAG_PAGE_PROCESS, FLAG_PAGE_EXCEPTION, FLAG_PAGE_UNCHECKED, FLAG_PAGE_AUDIT)
    annotation class FlagPageDef
}
Fan Applelin
  • 746
  • 1
  • 6
  • 16