I'm using #RestrictTo annotation to denote that a function should be used in only in subclasses or tests.
To do that I use the following syntax:
#RestrictTo(value = [SUBCLASSES, TESTS])
public override fun onCleared() {
// Expose protected fun onCleared for tests
}
At first it seemed to be working but my teammates reported Android Studio showing this warning:
I could reproduce this after building the project again.
This error goes away if I remove the TESTS scope from the annotation as if the annotation does not support multiple scopes in values.
Do you think if this is the intended behavior of the annotation?
Can you think another way to restrict a function to the union of two different scopes?
Thanks in advance
Related
I have the following kotlin property declared in a class of my Android app:
private val updateRef: KFunction<*> by lazy {
dao::class.functions.find {it.name.equals("update", true)}!!
}
Where dao is a reference to a Room DAO interface. Since I've updated kotlin to version 1.6.10 it doesn't work anymore, the following wierd exception is thrown every time I try to execute the code above. The same exception is thrown when I evaluate the expression using Android Studio's EVALUATE tool:
"Incorrect resolution sequence for Java method public open suspend fun count(): kotlin.Int defined in it.kfi.lorikeetmobile.db.dao.TablePriorityDao_Impl[JavaMethodDescriptor#d45ec9a]".
Where count() is a suspend method declared in the DAO interface. But I get this for every DAO class I have in my project and for different methods, so I think the method has nothing to do with the real problem here... I cannot figure out what is happening.
Before the update I had no problems at all. Please help.
In the end I discovered that the last version of kotlin-reflect (v. 1.6.10 in my case) doesn't keep backward compatibility for Kotlin classes that extend Java super-classes. Using Kotlin reflection to access member properties or functions of a Java super-class extended by a Kotlin class will fail as described in my question. It works fine if also the super-class is a Kotlin class. That is for me a bug that should be addressed.
So I am currently in the process of learning dagger 2, and from the tutorials that I've read so far, for a dependency to be injected, the #Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work, or if I don't want other parts of the code to know that dagger exists.
The application structure I have at the moment is:
App Module - where I'd like to put my DI code in (e.g. dagger modules, etc).
Presentation Module - Views/ViewModels etc.
Domain Module - Use Cases etc.
Data Module - Repositories etc.
With pretty much this style of classes contained in my application:
class ExampleViewModelImpl(useCase: ExampleUseCase): ViewModel() in Presentation (gets initialised from an Activity or similar).
class ExampleUseCaseImpl(repository: ExampleRepository): ExampleUseCase in Domain
class ExampleRepositoryImpl(dao: ExampleDao): ExampleRepository in Data
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible? Code examples of how this is achieved would be great.
I am unsure of some terminologies, and wasn't able to find a solution online. If there are good resources which explains what I'm asking, that would also be great.
if I don't want other parts of the code to know that dagger exists.
#Inject is a standard (JSR 330) which Dagger implements. Adding those annotations doesn't have anything to do with Dagger and can be used the same way with other DI frameworks. If it's your code you should just add those #Inject annotations where appropriate. Think of them as documentation: What constructor/field/method must be injected to create & use this object?
The only place where your classes will know that Dagger exists is at the same place where you'd be creating the objects otherwise, too.
Going down that path, of course you could use Dagger without any #Inject annotations, but you'd be writing a lot of unnecessary boilerplate and missing out on the most powerful feature of Dagger at the same time (code generation).
#Inject annotation gets placed inline with fields (for Activities/Fragments) or constructors. However I see that as an issue if I'm not the owner of parts of the code and can't add the required annotations for this technique to work
That's what #BindsInstance with the #Component.Builder is for (add an object to the component) and what #Provides annotated methods are for (create and initialize an object from a module)
If you really want to write code without #Inject, then you'd do exactly this for all of your objects. This means a lot of modules, and even more #Provides annotated methods. It will work, but I don't see the point in writing all those methods if a single #Inject on the constructor has the same effect.
In my opinion the best thing about Dagger is that I can add / remove / change constructor parameters and don't have to touch any other parts of my code since Dagger will generate new code with the new arguments. In your case you'd have to also change the parameters to the #Provides method as well as the constructor invocation.
Next let's look at how to remove #Inject from fields. Basically you don't want to do field injection, so instead of writing an injection method in the component, you'd write provision methods.
#Component
class MyComponent {
fun inject(activity: MyActivity)
}
class MyActivity {
#Inject lateinit var myDep: Dependency
fun onCreate() {
component.inject(this)
}
}
Removing the #Inject we need to use the provision methods instead.
#Component
class MyComponent {
fun provisionMyDependency() : Dependency
}
class MyActivity {
lateinit var myDep: Dependency
fun onCreate() {
myDep = component.provisionMyDependency()
}
}
It will work and everything, but again, you will miss out on the single best feature of Dagger: Code generation. The example above looks alright because I only added a single dependency, but think about what happens to those 2 different implementations when you add / remove / change dependencies, how well it will scale. If you prefer to do things manually any refactoring will become arduous.
With the structure above, what is the minimum number of classes outside of the App Module that I need to touch in order to utilize dagger with as much automated dependency injection as possible?
Your question (especially the title) is in direct conflict with your goal. If you don't want to use those annotations, then you can't use Dagger code generation & injection but have to resort to do it manually as highlighted above.
with as much automated dependency injection as possible
To best utilize Dagger you add #Inject on the constructor and/or fields of every class that should end up on your dependency graph and let Dagger do its thing.
In my Kotlin Multiplatform project, I'm trying to access Kotlin types defined in kotlin-stdlib from Swift.
TL;DR: StdLib types/methods seem not to result in header definitions, I'd like a solution that doesn't involve writing lots of boilerplate code
My scenario
I have an interface defined in Kotlin ...
interface MyKotlinInterface {
fun run() : Sequence<String>
}
... and implemented this interface in Swift ...
class MySwiftClass : MyKotlinInterface {
func run() -> KotlinSequence {
// return sequenceOf("foo")
}
}
... there I'm trying to create a Sequence but there are no methods from the kotlin.sequences package available (e.g. generateSequence).
Is it actually possible to access Kotlin framework types or methods beyond what I define in my code -- if yes, how? Furthermore, how can this be achieved without writing boilerplate code?
Further details
Having a look into the generated Objective-C header file, I see definitions for my class (obviously) and basic Kotlin types. What's missing is basically everything from the standard library functionality (I care for everything Sequence-related).
My build.gradle.kts looks like:
plugins {
kotlin("multiplatform") version "1.3.0"
}
kotlin {
targets { /* ... */ }
sourceSets {
getByName("commonMain") {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib-common")
}
}
// ...
getByName("iosMain") {
dependencies {
api("org.jetbrains.kotlin:kotlin-stdlib")
}
}
}
}
Having the kotlin-stdlib defined as a dependency for the iOS target, I would expect those to become actually available from Swift.
Minimal working example
https://github.com/panzerfahrer/so-mwe-kotlin-mpp-swift
Current solution approach
The only solution I came up with, is writing the desired function for the iOS target:
fun <T : kotlin.Any> generateSequence(nextFunction: () -> T?): kotlin.sequences.Sequence<T> = kotlin.sequences.generateSequence(nextFunction)
This works ok-ish but is highly unsatisfying as it requires lots of boilerplate code. Additionally, extension functions cannot be made available this way and would require more boilerplate code or even rewriting parts of the standard library.
Desired solution
I like to avoid writing boilerplate code as much as possible. What I actually only care about, is to have (in my case) Sequence fully accessible from Swift. My feeling is, it would be sufficient to make the compiler generate selected or all header definitions for the standard library functionality.
Do you really need lazy computation (aka Sequence) in your Kotlin code?
If no, I would recommend using List<T> instead (and it maps to Swift directly).
For Sequence implementation, a workaround could be to export a factory function from your Kotlin library, e.g. you may declare a function like
fun <T : kotlin.Any> generateSequence(nextFunction: () -> T?)
= kotlin.sequences.generateSequence(nextFunction)
You may select any other factory function for Sequence, that matches your use-case.
In general, there are too many functions in the Kotlin standard library. Exporting them all to Swift will create too many useless symbols in the binary and increase the compilation time.
This is a serious question, I promise. I've spent the last 2 hours reading as many definitions of Mock that I could find and none explain this to me.
I've got a class I want to test and that class requires a mapper class as part of it's primary constructor:
open class PoiListViewModel #Inject constructor(
private val mapper: PoiMapper
) : ViewModel() {
In my unit test I have the following code:
//Mock objects needed to instantiate the class under test
#Mock lateinit var mapper: PoiMapper
// Class being tested
lateinit var poiListViewModel: PoiListViewModel
#Before
fun setup() {
MockitoAnnotations.initMocks(this)
poiListViewModel = PoiListViewModel(mapper)
}
My question to you all smart developers is, what exactly is a mock? And specifically how much of my original class does it replicate?
I'll tell you my assumed definition. A mock is a fake stand-in class that stands in for my real class, but that it does nothing except keep track of what method calls get sent to it. If I want the mock to have any functionality I need to stub that functionality in.
At least that's my ignorant view of mocks. But I'm apparently wront because in my unit test, my "mock" mapper class seems to be an actual mapper class. If I debug my unit test I see it walk through all the code of my mapper class. I see it returning converted data.
Here's the mapper class code (if it matters):
open class PoiMapper #Inject constructor() {
fun mockTest(num: Int): Int{
return num *23
}
fun mapToPresentation(domainModel: Poi_Domain): Poi_Presentation {
var test = 3
var results = mockTest(test)
return Poi_Presentation(domainModel.id,domainModel.name,domainModel.description,
domainModel.img_url,domainModel.latitude,domainModel.longitude,domainModel.imgFocalpointX,
domainModel.imgFocalpointY,domainModel.collection,domainModel.collectionPosition,
domainModel.release,domainModel.stampText)
}
}
Can someone explain it to me, how much of a mock is a Mockito mock? Did I instantiate the mocks incorrectly? Can someone give me a better way to think of mocks so I can wrap my head around all this?
Your understanding of mocks is correct. You're bumping into Kotlin's final-by-default behavior, as an implementation detail of Mockito.
Mockito mocks (as distinct from Mockito spies) are defined to take the place of your class instance. Unless you've stubbed them to return otherwise, they record all interactions and return default dummy values (zero, an empty string, an empty list, null, etc). You can confirm that they collaborated correctly with your system-under-test by stubbing return values (when/thenReturn), stubbing specific behaviors for methods (when/thenAnswer), or by checking that certain methods were called (verify) and retrieving the specific instances they were called with (ArgumentCaptor).
Under the hood, Mockito achieves this by generating a subclass of the class you're mocking, and overriding all methods to delegate to Mockito's internal handler. This is what gives Mockito the power to override the behavior silently: Your system-under-test thinks it's calling your dependency, but your code is using Java's virtual method dispatch to override your dependency's behavior.
Here's the trick: Java methods are virtual by default, unless you mark them final. In Kotlin, functions are closed by default, unless you mark them open. (I'm going to keep calling them final, because that's the definition at play in the JVM or Android Dexer, which is reading the bytecode that Kotlin generates anyway.) When the virtual machine is sure of a reference's type based on static typing, and you're calling a final method, the compiler is allowed to inline that implementation's code (JLS 8.4.3.3) because you've asserted that the method cannot be overridden and any code that tries to override it will fail at compilation. Mockito's generated code isn't compiled this way, and Mockito can't detect this case.
In your case, mapToPresentation is not open, so the compiler sees it as final and does not keep the virtual method dispatch that would let Mockito override the behavior. Your definition of mocking is right, and your code would be right, except that Kotlin is closed-by-default where Java is open-by-default. Besides, you really do rely on the function being open, because you'd like it to be called with a different implementation than the one in the function.
Trivially, you could just make all functions open that you intend to override, or use Mockito 2.1+'s built-in feature to mock final methods. However, as a general best practice you want an object that behaves like a PoiMapper even if it doesn't follow your specific PoiMapper implementation. That might be a good reason for an interface/impl split, such that your class PoiListViewModel takes a PoiMapper interface. In production you can provide a PoiMapperImpl as you have, but Mockito can generate an arbitrary implementation of the PoiMapper interface without worrying about whether PoiMapperImpl and its functions are open or closed.
Have you added the annotation
#RunWith(MockitoJUnitRunner.class)
to your test class?
I recently converted the majority of my project to kotlin. Now I encounter several unusual errors that all seem to relate to annotation libraries. Needless to say, it didn't happen in Java.
I'll describe the cases - one in Dagger and one in Butterknife.
1.
When having 2 #Provides methods in different models with the same name.
For example in file "FooProvider.kt" having a "provideFooOrBar" method
#Module
class FooProvider(private val view: FooActivity) {
...
#Provides #FooScope fun provideView() = view
#Provides #FooScope fun provideFooOrBar() = Foo()
}
And having another file "BarProvider.kt" with the same method name
#Module
class BarProvider(private val view: BarActivity) {
...
#Provides #BarScope fun provideView() = view
#Provides #BarScope fun provideFooOrBar() = Bar()
}
In this case, Dagger fails to generate some factory libraries and I get the following compilation error:
Error:(27, 32) error: cannot find symbol class FooProvider_ProvideFooOrBarFactory
A sample project reproducing the issue can be found at https://github.com/maxandron/DaggerIssue325
2.
This is an issue when using Butterknife. When having two #Bind annotated variables in two different classes - One of them just fails to initialize at runtime without any compilation error!
For example if I have:
class FooActivity {
#Bind(R.id.foo) lateinit var mFoo: View
}
class NotFooActivity {
#Bind(R.id.not_foo) lateinit var mFoo: View
}
Then one of them (or both?) will just fail to initialize without any error. Causing a kotlin.UninitializedPropertyAccessException: lateinit property mFoo has not been initialized exception to be thrown when the field is accessed.
Is it something I'm doing wrong in configuring Kotlin or is it a kotlin bug?
Thank you in advance!
Ron
I was having this issue, so I started to investigate and it's caused because Kapt is only checking the method name when comparing them, and they are added in a set, thus duplicates are not allowed. The same happens for annotated fields, so currently you can have one method/field name per annotation.
I added the class name to the equals method and the annotations were properly handled now, but the tests broke and I don't know how they work, so I hope someone knows how to fix this.
It turned out to be a bug with kapt.
I posted an issue on Kotlin's bug tracker and the issue is now marked as fixed.
This solution was merged
Should be resolved in Kotlin version 1.0.2
So to somewhat answer the kotlin.UninitializedPropertyAccessException: lateinit issue, I was running into the exact same thing in my project. What I did which "solved the issue" for me was to remove Butterknife from the offending class, in this case it was just a viewHolder for my new expandable RecyclerView, and then run the app again.
Running the app, after switching all my #Bind(R.id.my_view_id) to the "old school" findViewById(R.id.my_view_id) as MyViewType worked, but then subsequently afterwards I switched the same class back to Butterknife and the UninitializedPropertyAccessException went away, and it seems like it won't come back unless something in the class changes, then you'll have to repeat this process again.
My suspicion here is that this has something to do with Kotlin not supporting incremental compilation, and somehow by changing the auto-generated code it was forced to recompile. But I could be way off here, just thought I'd share my experience.