Dagger 2 public field injection produces 'private field injection' error - kotlin

I'm trying to set up a very basic field injection using Dagger2 with the following structure:
class ToInject {}
class Injected {
#Inject
var toInject: ToInject? = null
}
#Module
object BaseModule {
var toInject: ToInject? = null
#Provides
#JvmStatic
fun toInjectProvider(): ToInject {
if (toInject == null) {
toInject = ToInject()
}
return toInject as ToInject
}
}
The field I'm trying to inject is definately PUBLIC but the compiler returns the following error
Dagger does not support injection into private fields
Can anyone please explain why am I getting this error and how to fix it?
BTW constructor injection works:
class Injected #Inject constructor(var toInject: ToInject){}

Try explicitly annotating the setter method:
class Injected {
#set:Inject
var toInject: ToInject? = null
}
or
class Injected {
var toInject: ToInject? = null
#Inject set
}
You can also annotate your field as #JvmField:
class Injected {
#JvmField
#Inject
var toInject: ToInject? = null
}
The problem is how Kotlin is translated to Java.
This Kotlin class:
class Injected {
var toInject: ToInject? = null
}
actually becomes this Java class:
public final class Injected {
#Nullable
private ToInject toInject;
#Nullable
public final ToInject getToInject() {
return this.toInject;
}
public final void setToInject(#Nullable ToInject value) {
this.toInject = value;
}
}
So despite the fact you set your field public in Kotlin, under the hood it's just a private field with a public setter and getter.

I am having similar issue while injecting primitive Int value. Only adding #JvmField worked for me. Thanks #jsamol for pointing out.
#JvmField
#Inject
#Named(NAMED_APP_LOGO)
var appLogo: Int = 0

Related

Kotlin: set annotated lateinit var using reflection

Imagine that I try to build simple dependency injection lib. Its Injector class, when called on a specific class, should inject all properties annotated with #Service annotation.
For example, given this client:
class ClientA {
#Service private lateinit var service1: Service1
#Service private lateinit var service2: Service2
private lateinit var service3: Service3
}
a call to injector.inject(ClientA()) should result in service1 and service2 being set (but not service3). Let's assume that Injector knows how to construct these objects.
My question is how to write the code that parses class' properties, checks their annotations and sets them in Kotlin?
Since I'm on Android, I tried to go through Java reflection:
fun inject(client: Any) {
val clientClass = client::class.java
val fields = clientClass.declaredFields
for (field in fields) {
if (isAnnotatedForInjection(field)) {
injectField(client, field)
}
}
}
private fun isAnnotatedForInjection(field: Field): Boolean {
val fieldAnnotations = field.annotations
for (annotation in fieldAnnotations) {
if (annotation is Service) {
return true
}
}
return false
}
The problem is that fieldAnnotations is empty. Converting ClientA's code to Java I see the following:
public final class ClientA {
private Service1 service1;
private Service2 service2;
private Service3 service3;
/** #deprecated */
// $FF: synthetic method
#Service
private static void service1$annotations() {
}
/** #deprecated */
// $FF: synthetic method
#Service
private static void service2$annotations() {
}
}
Looks like Kotlin compiler creates static methods to aggregate properties' annotations. With this info, I can write some ugly code to make it work using Java's reflection API, but there must be a cleaner way, right?
If you want to place the annotation on the field, you can use #field:Service.

Moq class with constructors ILogger and options netcore 2.1 vs2017 getting error

I need to mock a class that has parameters in the constructor by I cannot figure out how you do it using moq. It crashes
Constructor arguments cannot be passed for interface mocks.
See my attempt below:
[Fact]
public async Task MyTest()
{
var mySettings= GetMySettings();
var mySettingsOptions = Options.Create(mySettings);
var mockLogger = Mock.Of<ILogger<MyClass>>();
var mock=new Mock<IMyClass>(mySettings,mockLogger);
mock.Setup(x=>x.DoSomething(It.IsAny<string>().Returns("todo");
}
public class MyClass : IMyClass
{
private readonly ILogger<MyClass> logger;
private readonly MySettings mySettings;
public MyClass(IOptions<MySettings> settings,ILogger<MyClass>logger)
{
this.logger = logger;
this.mySettings = settings.Value;
}
public string DoSomething(string myarg)
{
//omitted
}
}
How do you do it? many thanks
EDITED
In order to mock repository and test the behaviour i also need to mock the other classes that have constructors in it. Hope makes sense
public class MyService:IMyService
{
private MyClass myclass;
private OtherClass otherClass;
private Repository repository;
public MyService(IRepository repository,IMyClass myclass,IMyOtherClass otherClass)
{
this.myclass=myClass;
this.otherClass=otherClass;
this.repository=repository;
}
public void DoStuff()
{
bool valid1=myclass.Validate(); //mock myclass
var valid2=otherClass.Validate(); //mock otherClass
if(Valid1 && valid2)
{
repository.GetSomething();//this is really what I am mocking
}
//etc..
}
}
It doesn't matter if your class constructor has parameters or not, because you're working with its mock object.
var mock = new Mock<IMyClass>();
mock.Setup(x=>x.DoSomething(It.IsAny<string>()).Returns("todo");
Then you can use this mock to your repository constructor:
var myService = new MyService(repositoryMock.Object, mock.Object, otherClassMock.Object);
You are getting this error because you are trying to create a mock of an interface (IMyClass in this case) with constructor values. It seems like you are trying to test the method in the class MyClass, therefore you should be creating a moq of this class.
To clarify change
var mock=new Mock<IMyClass>(mySettings,mockLogger); to var mock=new Mock<MyClass>(mySettings,mockLogger);

properties using cdi #Produces and #Inject with qualifiers

I am trying to come up with an easy to use CDI way to use properties. Based on several blogs this is the (not working) result (sorry for the layout cannot get that right).
1. EEProperties (the provider, seperate jar):
#Singleton
public class EEProperties extends AbstractPropertiesDecorator {
#Inject
public EEProperties(#InjectInEEProperties EnhancedMap properties) {
super(properties);
}
private String[] getKeys(final InjectionPoint ip) {
return (ip.getAnnotated().isAnnotationPresent(Property.class) && ip.getAnnotated().getAnnotation(Property.class).keys().length>0) ?
ip.getAnnotated().getAnnotation(Property.class).keys() :
new String[] {ip.getMember().getName()};
}
#Produces
#Property
public File[] getFileProperties(InjectionPoint ip) {
return getFileProperties(null, getKeys(ip));
}
}
2. A ManagedBean consumer (in war):
#Inject
#Property(keys = {"test"})
private String test;
3. ...that also produces the constructor argument for the provider:
#Produces
#InjectInEEProperties
public EnhancedMap getP() {
EnhancedMap m = new Settings();
m.put("test", "cdi works");
return m;
}
4. annotation for cdi container:
#Qualifier
#Retention(RUNTIME)
#Target({FIELD,ElementType.METHOD,ElementType.PARAMETER})
public #interface InjectInEEProperties {
#Nonbinding String key() default "";
}
5. annotation for consumer:
#Qualifier
#Retention(RUNTIME)
#Target({FIELD,ElementType.METHOD})
public #interface Property {
#Nonbinding String[] keys() default {};
}
6. problem when running this (on payara 5):
Caused by: java.lang.NullPointerException at
org.glassfish.weld.services.JCDIServiceImpl.createManagedObject(JCDIServiceImpl.java:463)
at
org.glassfish.weld.services.JCDIServiceImpl.createManagedObject(JCDIServiceImpl.java:314)
at
com.sun.enterprise.container.common.impl.managedbean.ManagedBeanManagerImpl.createManagedBean(ManagedBeanManagerImpl.java:476)
I've tried a lot of things, but cannot get this to work, including removing the #Produces from the ManagedBean.
Solved the issue by creating a seperate class responsible for providing constructor argument:
#Singleton
public class PP {
#Produces
#InjectInEEProperties
public EnhancedMap getP() {
EnhancedMap m = new Settings();
m.put("test", "cdi works");
return m;
}
}

Kotlin visibility of nested members

I have a class with a nested, private class. I have a Builder, standard Java builder pattern, that constructs instances of this class. I don't want anyone outside of my class to be able to see my hidden class.
In Java I could do this:
public class Example {
private SneakyType doNotExposeThis;
private Example(Builder builder) {
// OK 'cause in Java you can access the private
// members of a nested class
doNotExposeThis = builder.doNotExposeThis;
}
private static class SneakyType {
SneakyType(String x) {
// stuff
}
}
public static class Builder {
private SneakyType doNotExposeThis;
public void addFoo(String something) {
doNotExposeThis = new SneakyType(something);
}
public Example build() { return new Example(this); }
}
}
But I can't figure out how to do the same in Kotlin:
class Example(builder: Builder) {
private lateinit var doNotExposeThis: SneakyType
init {
doNotExposeThis = builder.doNotExposeThis
}
class Builder {
// If private or internal I can't access it in Example.init
// and if public it gets exposed.
val doNotExposeThis: SneakyType
fun addFoo(something: String) {
// actual construction is quite a bit more complex
doNotExposeThis = SneakyType(something)
}
}
}
Note that for the sake of Java interop I want to keep my builder. I also want it because my object is complicated to construct and I want it to be immutable so I have a builder with lots of setters, adders, vals, etc. and then in init I construct a single immutable Example.
The only alternatives I see are:
Instead of have a SneakyType in my builder save all the info necessary to construct one and then construct it in Example. Works but adds a ton of complexity.
Give up on Example being immutable and allow the builder to call into it to set up a Sneaky
Expose the Sneaky
Is there no way to mimic the Java version?
I see two viable options:
Use the internal visibility modifier:
class Example private constructor(builder: Builder) {
private val doNotExposeThis: SneakyType
init {
doNotExposeThis = builder.doNotExposeThis
}
internal class SneakyType(x: String)
class Builder {
internal lateinit var doNotExposeThis: SneakyType
fun addFoo(something: String) {
doNotExposeThis = SneakyType(something)
}
fun build(): Example {
return Example(this)
}
}
}
This will make SneakyType only visible within your Kotlin compilation module.
Make Example independent of its builder (this is what I recommend):
class Example private constructor(private val doNotExposeThis: SneakyType) {
private class SneakyType(x: String)
class Builder {
private lateinit var doNotExposeThis: SneakyType
fun addFoo(something: String) {
doNotExposeThis = SneakyType(something)
}
fun build(): Example {
return Example(doNotExposeThis)
}
}
}

Dagger: Inject named string in constructor

I have a properties file and I would like to inject a property in a service.
I would like use the constructor method for DI like this:
#Inject
public ScanService(#Named("stocks.codes") String codes, IYahooService yahooService) {
this.yahooService = yahooService;
this.codes = codes;
}
I try to do a module like specified in this link => Dagger: Inject #Named strings?
#Provides
#Named("stocks.code")
public String providesStocksCode() {
return "test";
}
And for the provider method for my service:
#Provides
#Singleton
public IScanService provideScanService(String codes, IYahooService yahooService){
return new ScanService(codes, yahooService);
}
When I run the compilation I get this error:
[ERROR]
/Users/stocks/src/main/java/net/modules/TestModule.java:[22,7]
error: No injectable members on java.lang.String. Do you want to add
an injectable constructor? required by
provideScanService(java.lang.String,net.IYahooService)
for net.modules.TestModule
How can I inject my property correctly in the constructor ?
Thanks.
You have two different names: stocks.codes and stocks.code.
You will also have to annotate your provideScanService codes parameter:
#Provides
#Singleton
public IScanService provideScanService(#Named("stocks.codes") String codes, IYahooService yahooService){
return new ScanService(codes, yahooService);
}
Or do it like this:
#Provides
#Singleton
public IScanService provideScanService(ScanService scanService){
return scanService;
}
If you mean Dagger 2, I can help you.
First you have to declare dependencies in Component
#Singleton
#Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
void inject(BaseActivity baseActivity);
#Named("cloud") UserDataSource userCloudSource();
#Named("disc") UserDataSource userDiscSource();
UserDataRepository userDataRepository();
}
then instantiate it in Module
#Module
public class ApplicationModule {
#Provides #Named("cloud")
UserDataSource provideCloudUserSource(UserCloudSource userSource) {
return userSource;
}
#Provides #Named("disc")
UserDataSource provideDiscUserSource(UserDiscSource userSource) {
return userSource;
}
#Provides
UserDataRepository provideUserRepository(UserDataRepository repository) {
return repository;
}
}
then inject it in constructor with #Named qualifiers
#Inject
public UserDataRepository(#Named("cloud") UserDataSource cloudSource,
#Named("disc") UserDataSource discSource) {
this.cloudDataSource= cloudSource;
this.discDataSource = discSource;
}