43

I have this code sample:

class MeasureTextView: TextView {
    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)

    companion object{
        val UNIT_NONE = -1
        val UNIT_KG = 1
        val UNIT_LB = 0            
    }

    fun setMeasureText(number: Float, unitType: Int){

        val suffix = when(unitType){
            UNIT_NONE -> {
                EMPTY_STRING
            }
            UNIT_KG -> {
                KG_SUFIX
            }
            UNIT_LB -> {
                LB_SUFIX
            }
            else -> throw IllegalArgumentException("Wrong unitType passed to formatter: MeasureTextView.setMeasureText")
        }

        // set the final text
        text = "$number $suffix"
    }
}

I want to be able to use, at compile time, the auto complete feature in conjunction with IntDef annotation, so when i invoke setMeasureText(...), the static variables are shown as options to the argument of this method.

I have searched about this, and i couldn't find if Kotlin supported this java-style annotations (intdef for example). So i have tried it, and made an annotation for this, but it won't show in autocompletion.

My question: - Is Java annotation IntDef supported in Kotlin (latest version)

  • If it is, how can i turn in ON in the Android Studio IDE (if it works, i can't get the compiler to suggest it).

  • If it is not, is there any Kotlin-way of make this compile time checks

johnny_crq
  • 4,291
  • 5
  • 39
  • 64
  • 3
    [Why don't you use `enum`](http://stackoverflow.com/questions/5143256/why-was-avoid-enums-where-you-only-need-ints-removed-from-androids-performanc)? – miensol Jun 15 '16 at 11:20
  • 3
    no reason just a pathetic one. Enums were discouraged a couple years ago by google due to performance reasons. I'm used to preferring static constants with intdef annotations. But when i changed to Kotlin that feature is gone – johnny_crq Jun 15 '16 at 12:08
  • 2
    Possible duplicate of [How to use Android Support typedef annotations in kotlin?](http://stackoverflow.com/questions/35976002/how-to-use-android-support-typedef-annotations-in-kotlin) – Dmytro Rostopira Feb 27 '17 at 12:53

7 Answers7

54

Strange thing, but this question comes in search before the same with right answer

Copying it here:

import android.support.annotation.IntDef
public class Test {

    companion object {

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

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

    @Speed
    private var speed: Int=SLOW

    public fun setSpeed(@Speed speed: Int) {
        this.speed = speed
    }
}
Community
  • 1
  • 1
Dmytro Rostopira
  • 10,588
  • 4
  • 64
  • 86
  • 27
    this does not solve anything. You can create annotation the same way as Java, but you cannot benefit from using IntDef on Kotlin because the compiler doesn't know how to interpret it. The advantage of IntDef is that, it will show in auto-completion and therefore, you can, most of the times, use IntDef contacts instead of enum. Take away the compiler part, and the annotation itself is useless. – johnny_crq Mar 01 '17 at 10:40
  • 3
    @user3806331 not agree. Autocomplete is not the only point. It's usefull, because annotated parameter is checked at compile time, so you can't call this function with `-1L` by mistake – Dmytro Rostopira Mar 01 '17 at 10:43
  • 13
    I don't think this is true. I've tested using a @StringDef as part one of my Kotlin class's constructor parameters and a compile time warning/error was never thrown when I passed an invalid type to it. – w3bshark Jul 17 '17 at 17:07
  • @DimaRostopira, I got your point but this code doesn't have completion either the compile-time annotation check. – Sanf0rd Jan 05 '19 at 00:17
  • 1
    It works for me. In my case i wanted String definition, so i've replaced IntDef by StringDef, i've defined the val as strings and is working right – Billyjoker Jul 11 '19 at 15:46
  • @johnny_crq hey there , is there any update to your comment now? – Ali Zarei Dec 15 '20 at 09:14
  • 1
    In current version of Android studio this annotation works, and does enforce checking assigning impossible value. – heshuimu Jun 29 '21 at 21:24
39

As of Kotlin 1.0.3, the @IntDef annotation is not supported, but support is planned for later versions.

The Kotlin way of making these compile time checks is to use an enum class instead of a series of Int constants.

yole
  • 92,896
  • 20
  • 260
  • 197
  • 18
    Aren't enums grossly inefficient on Android? – Raeglan Mar 13 '18 at 15:33
  • 11
    No, they are not. – yole Mar 13 '18 at 15:57
  • 12
    "For example, enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android." - https://developer.android.com/topic/performance/memory.html#Overhead – Raeglan Mar 13 '18 at 16:01
  • 3
    I wanted to know if the way Kotlin handles enums makes a difference. – Raeglan Mar 13 '18 at 16:02
  • @yole https://www.youtube.com/watch?v=Hzs6OBcvNQE (The price of ENUMs (100 Days of Google Dev)) – Mokkun Apr 03 '18 at 05:23
  • 22
    @Raeglan This advice is no longer relevant for ART; it has now been removed from the doc page you've linked to. – yole Apr 03 '18 at 10:49
  • 9
    @yole It has been removed from the memory advices, but not from here: https://developer.android.com/topic/performance/reduce-apk-size – hqzxzwb Aug 27 '18 at 11:04
  • 1
    Any news about `enum`s vs `IntDef` for apk size? Should we stick to `IntDef`? – LiTTle May 17 '19 at 09:13
  • 2
    It's true that enums have a bit bigger mem footprint in comparison to integers, but it's not a big deal anymore. You don't have a 100 enums anyway, plus ART now optimizes enum memory usage. But even if you're really constrained in terms of memory, note that if you use Proguard (or D8/R8) optimizations, it might convert simple enums to integers, so if you don't have any extra enum properties, this will most likely happen out-of-the-box. https://www.guardsquare.com/en/products/proguard/manual/usage/optimizations Look for `class/unboxing/enum`. – milosmns Aug 16 '19 at 08:22
  • 3
    @yole absolutely right! Using enums is fine now. Watch GoogleIO video: https://www.youtube.com/watch?v=IrMw7MEgADk&feature=youtu.be&t=857 – Daniel Oct 10 '19 at 09:42
  • 1
    @yole and 4 years later: when it will be available? – Metwalli Feb 14 '21 at 10:43
  • 1
    @Metwalli Never :). If it was not supported at the start, probably it won't be included for, at least, a decade. – Farid Feb 14 '22 at 18:04
9

My preferred way to use IntDef with Kotlin is to use top-level declarations:

package com.example.tips


const val TIP_A = 1
const val TIP_B = 2
const val TIP_C = 3

@IntDef(TIP_A, TIP_B, TIP_C)
@Retention(AnnotationRetention.SOURCE)
annotation class TipId


class TipsDataProvider {

    fun markTip(@TipId tipId: Int) {
    ...
    }
}

No extra class or object required! More info about top-level declarations here.

evi
  • 864
  • 1
  • 9
  • 18
3

If you are calling setMeasureText from Java you can get it to work by creating your IntDef in Java too

// UnitType.java
@Retention(RetentionPolicy.SOURCE)
@IntDef({MeasureText.UNIT_KG, MeasureText.UNIT_LB, MeasureText.UNIT_NONE})
public @interface UnitType {}

h/t Tonic Artos

You will also need to update your companion object to make your values longs and publicly accessible

companion object{
    const val UNIT_NONE = -1L
    const val UNIT_KG = 1L
    const val UNIT_LB = 0L
}
Enrico
  • 10,377
  • 8
  • 44
  • 55
2

Try it:

companion object {
    const val VALUE_1: Int = 1
    const val VALUE_2: Int = 2
    const val VALUE_3: Int = 3
}

@IntDef(value = [VALUE_1, VALUE_2, VALUE_3])
@Retention(AnnotationRetention.SOURCE)
annotation class TestClassName
ralphgabb
  • 10,298
  • 3
  • 47
  • 56
Hossein Kurd
  • 3,184
  • 3
  • 41
  • 71
2

Regarding the discussing koltin enum or java annotation, I made a simple test in android creating a library project and decompiling it to see the file sizes.

Kotlin enum class TestEnum:

enum class TestEnum {
     ENUM1,
     ENUM2,
     ENUM3,
     ENUM4,
     ENUM5,
     ENUM6,
     ENUM7,
     ENUM8,
     ENUM9,
     ENUM11,
     ENUM12,
     ENUM13,
     ENUM14,
     ENUM15,
     ENUM16,
     ENUM17,
     ENUM18,
}

Java interface TestInterface

@Retention(RetentionPolicy.SOURCE)
@StringDef(
        {
                ENUM1,
                ENUM2,
                ENUM3,
                ENUM4,
                ENUM5,
                ENUM6,
                ENUM7,
                ENUM8,
                ENUM9,
                ENUM11,
                ENUM12,
                ENUM13,
                ENUM14,
                ENUM15,
                ENUM16,
                ENUM17,
                ENUM18,
        }
)
public @interface TestInterface {
    public static final String ENUM1 = "ENUM1";
    public static final String ENUM2 = "ENUM2";
    public static final String ENUM3 = "ENUM3";
    public static final String ENUM4 = "ENUM4";
    public static final String ENUM5 = "ENUM5";
    public static final String ENUM6 = "ENUM6";
    public static final String ENUM7 = "ENUM7";
    public static final String ENUM8 = "ENUM8";
    public static final String ENUM9 = "ENUM9";
    public static final String ENUM11 = "ENUM11";
    public static final String ENUM12 = "ENUM12";
    public static final String ENUM13 = "ENUM13";
    public static final String ENUM14 = "ENUM14";
    public static final String ENUM15 = "ENUM15";
    public static final String ENUM16 = "ENUM16";
    public static final String ENUM17 = "ENUM17";
    public static final String ENUM18 = "ENUM18";
}

And if you build release (I am using default new android project gradle build) this is the result of the file size:

enter image description here

You decide on your own which one you like to use

If all your project is in Kotlin you don't have a choice and even resource annotations like @ColorRes don't work. Example: create a data class, annotate one property, try giving a wrong value, lint won't show an error

Ultimo_m
  • 4,724
  • 4
  • 38
  • 60
  • This is pretty interesting. What were your ProGuard rules related to enums? Did you use R8? And did you allow it to perform obfuscation? – VladimirVip Jun 18 '21 at 12:54
  • @VladimirVip just default stuff when you create an android library project with android studio – Ultimo_m Jun 20 '21 at 11:11
  • And how this "answer" is relevant? – Farid Feb 14 '22 at 18:07
  • @Farid It's a relevant hint for those who consider using enum or not. And in the end I have shown the status of Kotlin annotation at that time – Ultimo_m Feb 15 '22 at 18:49
1

As the accepted answer said,use an enum class in kotlin.

I wrote the specific code of the question asked,it may help some people new in kotlin:

class MeasureTextView: TextView {
   enum class UnitType(val value : Int){
       UNIT_NONE(-1),
       UNIT_KG(0),
       UNIT_LB(1)
   }

   fun setMeasureText(number: Float, unitType: UnitType){
       val suffix = unitType.value
   }
}
you
  • 51
  • 4