I'd like to leverage by to build class APIs in a nice way. Is there any way to do something like the following?
interface Foo<T> {
fun foo(t: T)
}
inline fun <reified T> createFoo() = object : Foo<T> {
override fun foo(t: T) {
// do stuff
}
}
// This is an error
class StringIntFoo : Foo<String> by createFoo(), Foo<Int> by createFoo()
fun main(){
val foo = StringIntFoo()
foo.foo("")
foo.foo(2)
}
// Doing it manually obviously isn't an issue
class ManualStringIntFoo {
fun foo(t: String){
}
fun foo(t: Int){
}
}
Link to a playground.
It looks like the generated method end up having the same JVM signature. I was hoping the reified types would get around it. With only a single implementation it works just fine and the types look correct.
Is there some way of actually doing this? Whether or not the StringIntFoo is technically a Foo I suppose isn't important for the problem at hand. It would be cool to be able to construct classes in this way.
Doing it manually doesn't work either if you try to actually implement the interfaces: ManualStringIntFoo : Foo<String>, Foo<Int> gives the same error as StringIntFoo.
So by can't help because it still compiles to class StringIntFoo : Foo<String>, Foo<Int> only setting the implementation of methods.
Related
I wanted to be able to define a method to clone an object that is the same type of itself. I define the interface requesting such, but the following does not compile or run.
interface Foo {
fun <T: Foo> copy() : T
}
class Bar(private val v:Int) : Foo {
override fun copy():Bar = Bar(v)
}
main() {
val bar1 = Bar(1)
val bar2 = bar1.copy()
}
If however I write the implementing class in Java, it will compile
class Bar implements Foo {
private int v;
public Bar(int v) {this.v = v;}
public Bar copy() {
return new Bar(v);
}
}
I can rewrite the code like the following that compiles:
interface Foo<out Foo>{
fun copy(): Foo
}
class Bar(private val v:Int) : Foo<Bar> {
override fun copy(): Bar = Bar(v)
}
However the following will fail with error: no type arguments expected for fun copy(): Foo
val newF = f.copy()
fun <T: Foo> addFoo(
foo: T,
fooList: List<T>,
): MutableList<T> {
val result: MutableList<T> = arrayListOf()
for (f in fooList) {
val newF = f.copy<T>()
result.add(newF)
}
result.add(foo)
return result
}
Is there a good solution to the problem?
The problem here is that Foo doesn't know the exact type of the implementing class, so has no way to specify that its method returns that same type.
Unfortunately, Kotlin doesn't have self types (see this discussion), as they would handle this situation perfectly.
However, you can get close enough by using what C++ calls the curiously-recurring template pattern. In Kotlin (and Java) you do this by defining Foo with a type parameter explicitly extending itself (including its own type parameter):
interface Foo<T : Foo<T>> {
fun copy(): T
}
Then the implementing class can specify itself as the type argument:
class Bar(private val v: Int) : Foo<Bar> {
override fun copy(): Bar = Bar(v)
}
And because T is now the correct type, everything else works out. (In fact, the : Bar is redundant there, because it already knows what the type must be.)
Your addFoo() method will then compile with only a couple of changes: give it the same type parameter <T: Foo<T>>, and remove the (now wrong, but unnecessary) type parameter when calling f.copy(). A quick test suggests it does exactly what you want (creates a list with clones of fooList followed by foo).
Since it's often useful for a superclass or interface to refer to the implementing class, this pattern crops up quite often.
BTW, your code is easier to test if Bar has its own toString() implementation, as you can then simply print the returned list. You could make it a data class, or you could write your own, e.g.:
override fun toString() = "Bar($v)"
I'm banging my head against the wall right now because I can't figure this out.
I have a generic Interface called Mapper which has two generic type parameters. Now I want to leverage multibinding and bind multiple implementations of this interface into a map of type Map<Class<out Any>, Provider<Mapper<Any, Any>>. My code looks as follows:
interface Mapper<DTO, Entity> {
fun toEntity(model: DTO): Entity
fun toDto(model: Entity): DTO
}
class PersistedIntakeEntryMapper #Inject constructor() : Mapper<PersistedIntakeEntry, IntakeEntry> {
override fun toEntity(model: PersistedIntakeEntry): IntakeEntry { TODO() }
override fun toDto(model: IntakeEntry): PersistedIntakeEntry { TODO() }
}
#Module
interface MapperModule {
#Binds
#IntoMap
#MapperKey(PersistedIntakeEntry::class)
#ModelMappers
fun bindPersistedIntakeEntryMapper(mapper: PersistedIntakeEntryMapper): Mapper<Any, Any>
}
#Singleton
class MapperFactory #Inject constructor(
#ModelMappers val mappers: Map<Class<out Any>, #JvmSuppressWildcards Provider<Mapper<Any, Any>>>,
) {
#Suppress("UNCHECKED_CAST")
inline fun <reified DTO: Any, Entity> get(): Mapper<DTO, Entity>? {
TODO()
}
}
Dagger is specifically complaining that PersistedIntakeEntryMapper is not assignable to Mapper<Any, Any>: MapperModule.java:13: error: #Binds methods' parameter type must be assignable to the return type.
However: the curious thing is that I have the same setup for another component which works like a charm:
interface ViewModelFactory<VM : ViewModel, SavedState, Parameters> {
fun create(savedState: SavedState?, parameters: Parameters?): VM
}
class SetCalorieGoalViewModelFactory #Inject constructor(
private val getCalorieGoalUseCase: GetCalorieGoalUseCase,
private val setCalorieGoalUseCase: SetCalorieGoalUseCase,
private val navigator: Navigator,
) : ViewModelFactory<SetCalorieGoalViewModel, SetCalorieGoalUiState, Nothing> {
override fun create(savedState: SetCalorieGoalUiState?, parameters: Nothing?): SetCalorieGoalViewModel {
TODO()
}
}
#Module
interface SetCalorieGoalUiModule {
#Binds
#IntoMap
#ViewModelKey(SetCalorieGoalViewModel::class)
fun bindSetCalorieGoalViewModelFactory(factory: SetCalorieGoalViewModelFactory)
: ViewModelFactory<ViewModel, Any, Any>
}
I can bind the SetCalorieGoalViewModelFactory to the ViewModelFactory<SetCalorieGoalViewModel, Any, Any> type without issue. What is the difference between these setups that makes one of them work and the other one not? I can't figure it out for the life of me. Big thanks in advance to anyone trying to solve this problem.
First of all, check out kotlin documentation on the generic variance topic as well as the related java topics (since dagger generates java code).
Generally the issue is that Mapper<PersistedIntakeEntry, IntakeEntry> and Mapper<Any, Any> are invariant, meaning that one is not subtype of the other. Basically this assignment val mapper: Mapper<Any, Any> = PersistedIntakeEntryMapper() will not compile and that's what dagger tells you. And that makes sense, since Mapper<Any, Any> must be able to map Any to Any and that's obviously not the case with PersistedIntakeEntryMapper - it expects PersistedIntakeEntry and IntakeEntry.
Following the documentation above, it would be possible if your declaration had out modifier specified like interface Mapper<out DTO, out Entity>, but that will not work in your case, since you have your type arguments in in positions.
The interesting question is why it works with ViewModelFactory. It seems to be a bug in KAPT, it just omits generic type parameters in the generated code when it sees Nothing. It makes it bypass the compiler checks (but it does not make it safe to use at runtime!), since generics are mostly compile-time things (see type erasure in java).
If I have a following interface:
interface BaseDataRemote<T, in Params> {
fun getData(params: Params? = null): Single<T>
}
Would it be possible have implementation of this interface that does not take Params?
To have effectively something like:
interface BaseDataRemote<T> {
fun getData(): Single<T>
}
Implementation is as follows:
class RemoteSellerDataSource #Inject constructor(
private val sellerApi: SellerApi,
#Named("LANG") private val lang: String
) : BaseDataRemote<SellerEntity, Nothing> {
override fun getData(params: Nothing?): Single<SellerEntity> {
return sellerApi.getSeller(lang).map { it.fromApiEntity() }
}
}
I use Dagger 2 to module to bind this implementation:
#Module
internal interface RemoteModule {
#Binds
#CoreScope
fun bindsSellerRemote(remoteSellerDataSource: RemoteSellerDataSource): BaseDataRemote<SellerEntity, Nothing>
}
I tried using Nothing as second type parameter, but it does not seem to work
(I'm getting required: class or interface without bounds error
Full error message:
RemoteSellerDataSource.java:6: error: unexpected type
public final class RemoteSellerDataSource implements com.bigchangedev.stamps.business.sdk.data.base.data.BaseDataRemote<SellerEntity, ?> {
^
required: class or interface without bounds
found:?
Thanks.
EDIT: the original answer was a pure Kotlin answer because the OP didn't mention Dagger.
Using Nothing is correct and works in pure Kotlin. However, Dagger seems to convert your code to Java, and in doing so it uses wildcards for the generics (which it doesn't like because it wants exact type matches). To avoid this issue, you can try using #JvmSuppressWildcards on your generic type parameters:
class RemoteSellerDataSource #Inject constructor(
private val sellerApi: SellerApi,
#Named("LANG") private val lang: String
) : BaseDataRemote<SellerEntity, #JvmSuppressWildcards Nothing> {
override fun getData(params: Nothing?): Single<SellerEntity> {
return sellerApi.getSeller(lang).map { it.fromApiEntity() }
}
}
Although I'm not sure what will happen in Java with Nothing in that case. I guess this should have the same effect on the Java code as removing the in variance for the second type param in the interface declaration, but without weakening your Kotlin types.
Another workaround would be to use Unit instead of Nothing, which Dagger will most likely convert to Void in this case. This is not great for your types, though.
Original answer:
You can technically already call getData() without arguments thanks to the default value. An implementation that doesn't care about the params argument can simply expect null all the time.
The Kotlin type that only contains null and no other value is technically Nothing?, and since getData is defined with Params? (note the ?) as input, it should be correct to specify Nothing (even without ?) as second type argument. So you should be able to define an implementation like this:
interface BaseDataRemote<T, in Params> {
fun getData(params: Params? = null): Single<T>
}
class ImplementationWithoutParams<T> : BaseDataRemote<T, Nothing> {
override fun getData(params: Nothing?): Single<T> {
// params will always be null here
}
}
To avoid confusion for the users, this implementation may additionally provide a getData() method without arguments at all:
class ImplementationWithoutParams<T> : BaseDataRemote<T, Nothing> {
override fun getData(params: Nothing?): Single<T> = getData()
fun getData(): Single<T> {
TODO("implementation")
}
}
My question might be noob but please help me. I don't understand what is purpose of not allowing non-open classes with "is" keyword in kotlin.
Sample code 1
fun main(){
val randomclassobject = RandomClass()
println(randomclassobject is someRandomInterface)
}
open class RandomClass{
}
interface someRandomInterface{
fun mustImplementThis()
}
The above code works perfectly fine
Now
Sample code 2
fun main(){
val randomclassobject = RandomClass()
println(randomclassobject is someRandomInterface)
}
class RandomClass{
}
interface someRandomInterface{
fun mustImplementThis()
}
without open keyword it shows the error "Error:(3, 34) Kotlin: Incompatible types: someRandomInterface and RandomClass"
Why open keyword really matters?
When you write it like this
class RandomClass {
}
interface SomeRandomInterface {
fun mustImplementThis()
}
It is not possible for any object to be an instance of both RandomClass and SomeRandomInterface because RandomClass itself does not implement SomeRandomInterface and it cannot have any subclasses that implement it either because it is not open (Kotlin classes by default cannot be extended unless you add open).
Since the compiler knows that this check cannot return true, it marks it as an error. Most other languages would probably just warn you that the check is useless, but Kotlin makes it illegal entirely.
On the other hand, when you write
open class RandomClass {
}
interface SomeRandomInterface {
fun mustImplementThis()
}
even though the class itself does not implement the interface, it could have a subclass that implements it, for example
open class RandomClass {
}
interface SomeRandomInterface {
fun mustImplementThis()
}
class RandomSubClass : RandomClass(), SomeRandomInterface {
fun mustImplementThis() {}
}
which means that the check can return true, so the compiler allows it in that case.
What's the best way to get an instance of a generic type in Kotlin? I am hoping to find the best approximation of the following C# code:
public T GetValue<T>() where T : new() {
return new T();
}
EDIT: As mentioned in comments, this is probably a bad idea. Accepting a () -> T is probably the most reasonable way of achieving this. That said, the following technique will achieve what you're looking for, if not necessarily in the most idiomatic way.
Unfortunately, you can't achieve that directly: Kotlin is hamstrung by its Java ancestry, so generics are erased at run time, meaning T is no longer available to use directly. Using reflection and inline functions, you can work around this, though:
/* We have no way to guarantee that an empty constructor exists, so must return T? instead of T */
inline fun <reified T : Any> getValue(): T? {
val primaryConstructor = T::class.constructors.find { it.parameters.isEmpty() }
return primaryConstructor?.call()
}
If we add some sample classes, you can see that this will return an instance when an empty constructor exists, or null otherwise:
class Foo() {}
class Bar(val label: String) { constructor() : this("bar")}
class Baz(val label: String)
fun main(args: Array<String>) {
System.out.println("Foo: ${getValue<Foo>()}") // Foo#...
// No need to specify the type when it can be inferred
val foo : Foo? = getValue()
System.out.println("Foo: ${foo}") // Foo#...
System.out.println("Bar: ${getValue<Bar>()}") // Prints Bar#...
System.out.println("Baz: ${getValue<Baz>()}") // null
}