Generics Kotlin强制参数类型为字符串、布尔值或数字

Generics Kotlin强制参数类型为字符串、布尔值或数字,generics,kotlin,primitive-types,Generics,Kotlin,Primitive Types,我有一个用例,我向我的服务器发送一些数据(作为分析),这些数据总是String、Boolean或Number 如何强制调用方只发送数字、布尔值或字符串而不发送任何其他对象 以下情况应该有效- userProperties: MutableMap<String, in AnyPrimitive> = mutableMapOf(), userProperties.put("someKey", 1) userProperties.put("someKey", 1.2f) userProp

我有一个用例,我向我的服务器发送一些数据(作为分析),这些数据总是
String
Boolean
Number

如何强制调用方只发送数字、布尔值或字符串而不发送任何其他对象

以下情况应该有效-

userProperties: MutableMap<String, in AnyPrimitive> = mutableMapOf(),

userProperties.put("someKey", 1)
userProperties.put("someKey", 1.2f)
userProperties.put("someKey", "someValue")
userProperties.put("someKey", true)
我尝试的方法是创建一个抽象类
EventData
,它实现
CharSequence
Number
。但这要求每个人都创建这些类的实例,而不是简单地发送数字或字符串


我可以设置一个逻辑来检查类型并抛出异常,但我更希望在编译时限制它。

如果您想从使用角度简化,请按如下方式声明send函数:

fun send(arg: Any) {
    //Validations here:
    if (!validate(arg)) throw IllegalArgumentException("...")

    //Actual send code here...
}

private fun validate(arg: Any): Boolean {
    return (arg is String || arg is Boolean || arg is Number)
}
请注意,这不是在编译时强制执行的,而是运行时失败。所以如果有人编了一个密码

send(RandomObject)

编译将成功。但在运行时它将失败。

根据al3c的注释,有三个重载。在kotlin中使用泛型无法实现您指定的功能

fun send(arg: Number) {
    // send
}

fun send(arg: Boolean) {
    // send
}

fun send(arg: String) {
    // send
}

// Many properties gathered in map described below
fun send(arg: EventData) {
    // send
}
如果要使用地图对象发送分析。这需要一个新类来包含上述三个重载。约束该类的用户添加错误类的任何内容

class EventData {

    // Or depending on other requirements you could use three maps with specific type
    private val analyticsData = mutableMapOf<String, Any>()

    fun put(key: String, arg: Number) {
         analyticsData.put(key, arg)
    }

    fun put(key: String, arg: Boolean) {
         analyticsData.put(key, arg)
    }

    fun put(key: String, arg: String) {
         analyticsData.put(key, arg)
    }

}
类事件数据{
//或者根据其他要求,您可以使用三个特定类型的地图
private val analyticsData=mutableMapOf()
趣味put(键:字符串,参数:数字){
分析数据输入(键,参数)
}
趣味输入(键:字符串,参数:布尔值){
分析数据输入(键,参数)
}
趣味put(键:字符串,参数:字符串){
分析数据输入(键,参数)
}
}

我早该想到的。我可以编写自定义注释和注释处理器,它将在编译时检查参数的类型。像这样的-

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface SupportedTypes {

  Class<?>[] types() default {};
}
@Retention(RetentionPolicy.CLASS)
@目标(ElementType.TYPE)
public@interface支持的类型{
类[]类型()默认值{};
}
并编写一个自定义注释处理器,该处理器将检查传入
types()
的所有项目的类型


我将在编写自定义注释处理器时给出答案。同时,您可以参考

我最好的建议如下:

密封类数据{
类StringData(val s:String):Data()
类NumberData(val n:Int):数据()
类BooleanData(val b:Boolean):Data()
伴星{
(s:String)=StringData的乐趣
乐趣(n:Int)=数字数据(n)
(b:布尔)=布尔数据(b)的乐趣
}
}
然后您的代码片段变成:

userProperties: MutableMap<String, Data> = mutableMapOf(),

userProperties.put("someKey", Data.of(1))
userProperties.put("someKey", Data.of("someValue"))
userProperties.put("someKey", Data.of(true))
userProperties:MutableMap=mutableMapOf(),
userProperties.put(“someKey”,Data.of(1))
userProperties.put(“someKey”,Data.of(“someValue”))
userProperties.put(“someKey”,Data.of(true))

这在多大程度上是可行的实际上取决于代码的其余部分。

只需提供3个重载。@al3c我想让问题更简单,所以最终编写了伪代码。更新问题。在这种情况下,一个带有3个实现的
密封类应该可以。@al3c但在这种情况下,开发人员必须传递这些类的实例,而不是简单地传递原语。如果我遗漏了什么,请将其作为答案发布,我会将其标记为已接受。注意:如果您强制调用方只发送一个数字、布尔值或字符串,他们仍然可以使用以下适配器发送
任何
对象:
类ToNumberAdapter(val actualObject:Any):number(){//number方法实现}
嗨,亚当,我想我的问题不清楚。我已经更新了这个问题,并且在这里发布了一个可能的答案。问题是开发人员在每次添加内容时都必须使用Data.of()。没有直接使用原语那么简单,这是我的主要问题。@Rajkiran您可以创建以下扩展方法:
operator fun MutableMap.set(key:K,string:string){this[key]=Data.of(string)}
operator fun MutableMap.set(key:K,number:number){this[key]=Data.of(number)}
,和
操作符fun MutableMap.set(key:K,boolean:boolean){this[key]=Data.of(boolean)}
@Bananon这实际上很聪明。这里唯一的问题是人们每次都必须调用这个扩展方法,而不是简单地将值放在里面。但仍然是一个好方法。@Rajkiran您认为调用扩展方法不如调用成员方法方便吗?
userProperties: MutableMap<String, Data> = mutableMapOf(),

userProperties.put("someKey", Data.of(1))
userProperties.put("someKey", Data.of("someValue"))
userProperties.put("someKey", Data.of(true))