Kotlin中Dagger使用@Named限定符报错

有时候我们需要Dagger为我们提供某个类型的两个不同的实例,可以使用@Named限定符来实现。比如本例中我们需要两只狗,先用@Module注解定义了一个模块,其中使用@Providers注解告诉Dagger可以提供Dog类实例,并且还使用了@Named(xxx)注解区分,否则编译会报错,Dagger不允许多处提供同一类型的实例,但使用命名后,会生成不同的工厂类和提供实例方法。

@Module
class AnimalsModule {

    @Provides
    @Singleton
    @Named("SingleDog")
    fun providerSingleDog(): Dog {
        return Dog()
    }

    @Provides
    @Singleton
    @Named("LickDog")
    fun providerLickDog(): Dog {
        return Dog()
    }
}

在DemoZoo类中定义了需要注入依赖的属性

class DemoZoo {
    @Inject
    @Named("SingleDog")
    lateinit var singleDog: Dog

    @Inject
    @Named("LickDog")
    lateinit var lickDog: Dog
}

看着没啥问题,编译,哦豁,报错

 [Dagger/MissingBinding] com.example.demo.Dog cannot be provided without an @Inject constructor or an @Provides-annotated method.

public abstract interface DemoDaggerComponent {

                ^

      com.example.demo.Dog is injected at

          com.example.demo.DemoZoo.dog

大致就是  DemoZoo类需要Dagger提供Dog实例,但是没有找到可以提供的地方。

正常应该是用@Named("LickDog")注解的lickDog属性,所需要的依赖对象是由@Provides,@Named("LickDog")注解提供的,但错误信息却是找不到。

百度和Google搜索了下,都是提到@Named(XXX)要改成@field:Named(XXX), 然后修改了一下,的确编译没问题了,但是这样做又是为什么呢,查看了Kotlin的文档,有提到@field注解的用法。

在Kotlin中, var xxx的属性,生成的Java字节码会有多个地方,比如getXXX()、setXXX()、成员变量xxx, kotlin编译器就需要借助注解使用处目标来精确指定如何生成该注解,比如@field:就是说后面跟的注解要用在属性变量上。但是,问题又来了,那为啥@Inject注解不需要?

前面截图下面还提到

如果不指定使用处目标,则根据正在使用的注解的 @Target 注解来选择目标 。如果有多个适用的目标,则使用以下列表中的第一个适用目标:

  • param
  • property
  • field

点击@Inject看到定义是这样的,申明了@Target, 可以用在方法、构造函数和属性元素上

@Target({ METHOD, CONSTRUCTOR, FIELD })
@Retention(RUNTIME)
@Documented
public @interface Inject {}

所以@Inject lateinit var wangCai: Dog中

@Inject虽然没有用@filed:指定,但编译器会使用@Target注解来选择目标,这里应该是有三个适用目标,分别是get、set和field,根据第一适用目标规则就是filed,所以在Java字节码中是在属性上进行注解。

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Named {

    /** The name. */
    String value() default "";
}

@Named没有申明@Target,编译器不知道如何处理,会忽略该注解,生成的Java字节码没有该注解。

查看kotlin 字节码反编译后的java代码存在@Inject,但没有@Named注解。

public final class DemoZoo {
   @Inject
   public Dog singleDog;
   @Inject
   public Dog lickDog;
   @Inject
   public DogCatBug catBug;
   @Inject
   public Cat cat;
}

修改代码,作为对比,一个改为@field:Named,一个保留原样

//DemoZoo.kt
class DemoZoo {
    @Inject
    @Named("SingleDog")
    lateinit var singleDog: Dog

    @Inject
    @field:Named("LickDog")
    lateinit var lickDog: Dog
}

//kotlin bytecode decompile
public final class DemoZoo {
   @Inject
   public Dog singleDog;
   @Inject
   @Named("LickDog")
   public Dog lickDog;
   @Inject
   public DogCatBug catBug;
   @Inject
   public Cat cat;
}

使用@field:Named语法的注解出现在了java字节码里,所以能被Dagger处理。

有了以上的了解,我尝试自定义了一个CusNamed注解,就是在Named类的基础上增加了@Target,然后把刚才使用@Named注解的地方替换成@CusNamed,重新编译也没有问题。

@Target({ METHOD, FIELD })
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface CusNamed {

    /** The name. */
    String value() default "";
}

问题的根本原因,实际上就是Named注解没有定义Target,然后呢Kotlin元素生成Java元素时对注解的处理方式导致的,这里还是建议使用@field:Named语法,毕竟很多项目都这样写。

  • 9
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Kotlin,@Qualifier注解通常用于与依赖注入框架一起使用,以标识特定的依赖项。在使用依赖注入框架时,您可以使用@Qualifier注解来指定要注入的依赖项的特定实现。 以下是一个简单的示例,演示如何使用@Qualifier注解: ```kotlin interface MyDependency class MyFirstDependency : MyDependency { override fun toString(): String { return "MyFirstDependency" } } class MySecondDependency : MyDependency { override fun toString(): String { return "MySecondDependency" } } class MyComponent { @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class FirstDependency @Qualifier @Retention(AnnotationRetention.RUNTIME) annotation class SecondDependency @Inject @FirstDependency lateinit var firstDependency: MyDependency @Inject @SecondDependency lateinit var secondDependency: MyDependency } fun main(args: Array<String>) { val component = DaggerMyComponent.builder().build() println(component.firstDependency) // prints "MyFirstDependency" println(component.secondDependency) // prints "MySecondDependency" } ``` 在上面的示例,我们定义了一个MyDependency接口,然后实现了两个具体的实现MyFirstDependency和MySecondDependency。接下来,我们定义了一个MyComponent类,其包含了@FirstDependency和@SecondDependency注解来标识要注入的特定依赖项。最后,我们使用Dagger2库生成MyComponent实例,并打印出注入的依赖项。 请注意,@Qualifier注解本身并不提供任何特定的注入功能,而只是用于标识特定的依赖项。实际的注入功能由依赖注入框架提供。在上面的示例,我们使用Dagger2库来实现依赖注入。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值