Kotlin: set annotated lateinit var using reflection - kotlin

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.

Related

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

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

This not referring to the original object using Kotlins class delegation

I am confused how delegation works in Kotlin. Wikipedia says:
With language-level support for delegation, this is done implicitly by having self in the delegate refer to the original (sending) object, not the delegate (receiving object).
Given the following Code:
interface BaseInterface {
fun print()
}
open class Base() : BaseInterface {
override fun print() { println(this) }
}
class Forwarded() {
private val base = Base()
fun print() { base.print() }
}
class Inherited() : Base() {}
class Delegated(delegate: BaseInterface) : BaseInterface by delegate
fun main(args: Array<String>) {
print("Forwarded: ")
Forwarded().print();
print("Inherited: ")
Inherited().print();
print("Delegated: ")
Delegated(Base()).print();
}
I get this output:
Forwarded: Base#7440e464
Inherited: Inherited#49476842
Delegated: Base#78308db1
I'd expect Delegated to return Delegated because self/this should refer to the original object. Do I get it wrong or is Kotlins delegation different?
Kotlin delegation is very simple - it generates all interface methods and implicitly invokes it on delegated object, except for methods explicitly overriden by the user.
Your example is functionally the same as:
class Delegated(delegate: BaseInterface) : BaseInterface{
// when generating bytecode kotlin assigns delegate object to internal final variable
// that is not visible at compile time
private val d = delegate
override fun print(){
d.print()
}
}
So it's pretty clear why it prints Base.
I think this is easiest to understand if we look at the decompiled Java bytecode this gets compiled into:
You can do this by going to Tools > Kotlin > Show Kotlin Bytecode and then clicking Decompile
public final class Delegated implements BaseInterface {
// $FF: synthetic field
private final BaseInterface $$delegate_0;
public Delegated(#NotNull BaseInterface delegate) {
Intrinsics.checkParameterIsNotNull(delegate, "delegate");
super();
this.$$delegate_0 = delegate;
}
public void print() {
this.$$delegate_0.print();
}
}
So when you do interface delegation what happens is that Kotlin creates field for the delegate named $$delegate_0 and adds methods in your delegating class which will operate on $$delegate_0. You can have multiple delegates as well, they will get their own fields. There is one caveat though: you can't access $$delegate_0 directly, not even if you make it a var like this:
class Delegated(var delegate: BaseInterface) : BaseInterface by delegate
This will compile to:
public final class Delegated implements BaseInterface {
#NotNull
private BaseInterface delegate;
// $FF: synthetic field
private final BaseInterface $$delegate_0;
#NotNull
public final BaseInterface getDelegate() {
return this.delegate;
}
public final void setDelegate(#NotNull BaseInterface var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.delegate = var1;
}
public Delegated(#NotNull BaseInterface delegate) {
Intrinsics.checkParameterIsNotNull(delegate, "delegate");
super();
this.$$delegate_0 = delegate;
this.delegate = delegate;
}
public void print() {
this.$$delegate_0.print();
}
}
sadly. I've written about this topic here.

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);

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)
}
}
}

Replace #Value property within #Configuration during Spring Boot test

Scenario
I've got a Spring Boot application with a #Configuration annotated Spring configuration class which contains some #Value annotated fields. For testing I want to replace these field values with custom test values.
Unfortunately these test values cannot be overridden using a simple properties file, (String) constants or similar, instead I must use some custom written property resolving Java class (e.g. TargetProperties.getProperty("some.username")).
The problem I have is that when I add a custom PropertySource to the ConfigurableEnvironment within my test configuration, it's already too late because this PropertySource will be added after the e.g. RestTemplate has been created.
Question
How can I override #Value annotated fields within a #Configuration class with properties obtained programmatically via custom Java code before anything else gets initialized?
Code
Production Configuration Class
#Configuration
public class SomeConfiguration {
#Value("${some.username}")
private String someUsername;
#Value("${some.password}")
private String somePassword;
#Bean
public RestTemplate someRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(
new BasicAuthorizationInterceptor(someUsername, somePassword));
return restTemplate;
}
}
Test Configuration Class
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SomeTest {
#SpringBootConfiguration
#Import({MySpringBootApp.class, SomeConfiguration.class})
static class TestConfiguration {
#Autowired
private ConfigurableEnvironment configurableEnvironment;
// This doesn't work:
#Bean
#Lazy(false)
// I also tried a #PostConstruct method
public TargetPropertiesPropertySource targetPropertiesPropertySource() {
TargetPropertiesPropertySource customPropertySource =
new TargetPropertiesPropertySource();
configurableEnvironment.getPropertySources().addFirst(customPropertySource);
return customPropertySource;
}
}
}
You can override properties directly in the #SpringBootTest annotation using the properties parameter:
#SpringBootTest(properties = {"some.username=user", "some.password=pwd"},
webEnvironment = SpringBootTest.WebEnvironment.NONE)
You can use #TestPropertySource
#TestPropertySource(
properties = {
"some.username=validate",
"some.password=false"
}
)
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
//...
}
You can use constructor injection in production cases, which allows it to set the configuration manually:
#Configuration
public class SomeConfiguration {
private final String someUsername;
private final String somePassword;
#Autowired
public SomeConfiguration(#Value("${some.username}") String someUsername,
#Value("${some.password}") String somePassword) {
this.someUsername = someUsername;
this.somePassword = somePassword;
}
...
)
}
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SomeTest {
private SomeConfiguration config;
#Before
public init() {
config = new SomeConfiguration("foo", "bar");
}
}