首页 > 解决方案 > 使用注解启用 Kotlin typealias 类型检查

问题描述

Kotlin 具有类型别名,在您想要一个好的命名时非常方便。但是,在某些情况下,您希望有一个比别名多一点的类型别名,您希望它强制执行实际的编译时检查,而无需创建新类。

这就是我想要实现的目标:

typealias MyNum = Int

fun isMagical(num: MyNum) = num == 42

fun main() {
    // Should fail/warn
    isMagical(42)
    // Should pass
    isMagical(42 as MyNum)

    // Should fail/warn
    val x = 3
    isMagical(x)

    // Should pass
    val y: MyNum = 3
    isMagical(y)
}

我知道我可以使用内联类来实现这一点,但我需要检查其中的许多类型,并且不想为每个类型创建一个类。

可以通过注释来实现吗?喜欢:

@Target(AnnotationTarget.TYPEALIAS)
annotation class StrongType

@StrongType
typealias MyNum = Int

然后让注释处理器进行检查?

我想做一些类似于 Android 的事情@IntDef

// Android way (performant but needs to manually annotates methods and list the options in the annotation)
@Retention(SOURCE)
@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
public @interface NavigationMode {}
public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;
// Enum way (not performant)
enum class NavMode constructor(val value: Int){
    STANDARD(0),
    LIST(1),
    TABS(2)
}

// Inline class (performant but generating a lot of code)
inline class NavMode(val value: Int) {
    companion object {
        val STANDARD = NavMode(0)
        val LIST = NavMode(1)
        val TABS = NavMode(2)
    }
}

// This is what I would like: performant, type check in methods without annotating them, clean code
@StrongType
typealias NavMode = Int
const val STANDARD: NavMode = 0
const val LIST: NavMode = 1
const val TABS: NavMode = 2

请注意,这不是我真正的用例,我有很多这样的枚举要创建,同时保持高性能(就像在 android API 中一样)。您认为实现我想要的最可行的方法是什么?谢谢

标签: javaclasskotlinannotationstype-alias

解决方案


typealias MyNum = Int表示这MyNum是 的另一个名称Int,差不多就是这样。您的“应该警告”检查不起作用,因为 a MyNumis anInt和 an Intis a MyNum-就类型系统而言,两者之间没有区别。

如果您希望类型系统将它们视为单独的事物,那么您实际上需要一个单独的类型,如果您只想要一个“特殊的 Int”,您会遇到问题,因为Int它是一个最终类,不幸的是您不能将其子类化. 因此,您将无法仅将其MyInt视为Int.


您使用 IntDefs、枚举等的示例有些不同 - 您肯定需要一种新类型,具有一组有限的可能的预定义值。有些语言会允许你这样做,但仍然将其视为Int- Kotlin 不会。

IntDefs 可能最接近您想要的,您必须在其中注释所有内容,因为这是检查它的方式 - 它在类型系统之外。它很笨拙,但那是因为它是固定的。

密封类可以为您提供所需的“干净”定义:

// or an interface
abstract class MyInt() {
    abstract val value: Int
}

// put them inside the sealed class (in {}) if you want them named NavMode.LIST etc
sealed class NavMode(override val value: Int) : MyInt()
object STANDARD : NavMode(0)
object LIST : NavMode(1)
object TABS : NavMode(2)

但这基本上是一个没有枚举的枚举(您可以利用它来确保每个值都是唯一的 - 这里没有检查,您可以为每个值传递 0)


你说你有“效率限制”,但是你呢?就像,在内存中拥有每个枚举的一个实例实际上是一个问题,或者持有对对象而不是原语的引用?IntDefs 是(是吗?这些天你没有听到太多关于它的消息......)推荐的避免这些对象分配和引用的方法,但它是与更复杂的代码的权衡。

枚举很简单,有时这更重要。我建议先进行一些分析,看看使用它们实际上会产生多大的影响,然后再抛出可能是最好的解决方案。只是值得思考!


推荐阅读