I am exploring and actively using generics in production with Kotlin.
Kotlin + generics is a big puzzle for me, so maybe you can explain and help me understand how it works here, compared to Java.
I have class AbstracApiClient (not really abstract)
class AbstracApiClient {
open protected fun makeRequest(requestBuilder: AbstractRequestBuilder) {
// ...
}
}
AbstractRequestBuilder (not really abstract):
open class AbstractRequestBuilder {
...
}
ConcreteApiClient that inherits AbstractApiClient that should override makeRequest with ConcreteRequestBuilder inherited from AbstractRequestBuilder:
class ConcreteApiClient: AbstractApiClient() {
protected override fun makeRequest(requestBuilder: ConcreteRequestBuilder) {
// ...
}
}
class ConcreteRequestBuilder: AbstractRequestBuilder()
As I would have more concrete API clients. I would like to make an abstraction that I can pass inherited concrete requests builders and override `make requests method.
I tried using it as it is but wouldn't work
I tried this notation protected open fun <R: ApiRequestBuilder> make request(request builder: R) but it won't match overriding function which I want it to be: protected override fun make request(request builder: ConcreteRequestBuilder)
What other options do I have? Am I missing something here?
Note: I cannot use interface or abstract classes in this scenario, so ideally I would like to find a way with inheritance and functions overriding.
You can't override a method with more specific argument types, because it breaks Liskov's substitution principle:
val client: AbstractApiClient = ConcreteApiClient()
client.makeRequest(AbstractRequestBuilder())
As you can see above, the ConreteApiClient implementation has to be able to handle all possible inputs of the parent class, because it could be accessed through the parent class's API.
To do what you want, you need to restrict the parent class itself via generics:
open class AbstractApiClient<R : AbstractRequestBuilder> {
open protected fun makeRequest(requestBuilder: R) {
// ...
}
}
class ConcreteApiClient: AbstractApiClient<ConcreteRequestBuilder>() {
protected override fun makeRequest(requestBuilder: ConcreteRequestBuilder) {
// ...
}
}
This way, any instance of AbstractApiClient<R> has to show which type of request builder it accepts (in the type argument). It prevents the above issue because now the parent type also carries information:
// doesn't compile
val client: AbstractApiClient<AbstractRequestBuilder> = ConcreteApiClient()
// this compiles
val client: AbstractApiClient<ConcreteRequestBuilder> = ConcreteApiClient()
I tried this notation protected open fun <R: ApiRequestBuilder> make request(request builder: R)
Now regarding this attempt, it doesn't work because if you make the method generic (not the class) it means every implementation of the method has to handle all kinds of R (NOT one R per implementation). Putting the generic on the class allows to specify the generic argument once per instance of the class.
Related
I have an interface Persistable which looks like this, the <T extends Statement<T>> List<Statement<T>> is to allow it to support both BoundedStatements and SimpleStatements in data stax 4.x driver.
public interface Persistable {
<T extends Statement<T>> List<Statement<T>> statements();
}
This java interface is inherited by Kotlin class A such that
data class UpdateRule(
private val something: S) : Persistable {
override fun statements(): List<Statement<BoundStatement> {
return PutKeyValue(Function(orgId, serviceId), JsonUtil.toJson(rule)).statements() //this returns BoundStatement
}
}
However, this gives the error Conflicting overloads.This code seems to work in Java(although with a warning), but in Kotlin it does not allow at all, how can I resolve this while also making sure parent interface remains generic to both Bound and Simple Statement?
You seem to misunderstand what the generics in Persistable mean. As it is written right now, you are supposed to implement the statements method so that it can handle any kind of T that extends Statement<T>. The generics there doesn't mean "implement this by choosing a kind of statement that you like".
It only produces a warning in Java because Java's generics is broken. Because of type erasure, List<Statement<BoundStatement> and List<Statement<T>> both erase to the same type - List, so the method in UpdateRule does implement the method in the interface if you consider the erasures. OTOH, type erasure isn't a thing in Kotlin (at least not in Kotlin/Core).
To fix this, you can move the generic type parameter to the interface:
public interface Persistable<T extends Statement<T>> {
List<Statement<T>> statements();
}
data class UpdateRule(private val something: S) :
Persistable<BoundStatement> {
override fun statements(): List<BoundStatement> =
PutKeyValue(Function(orgId, serviceId), JsonUtil.toJson(rule)).statements()
}
Notice how when we are implementing the interface, we can now specify the specific T that we are implementing for.
In Java just like in Kotin, the value of the type parameter of a generic method is determined by the caller of the method, and can be different at every call of the method, even on the same instance.
In your specific case, with the Java interface declared like this, statements() is supposed to be implemented in such a way that the caller can choose which type of statement will be returned by a given call to this method. This is not the case in your implementation, and that's why Kotlin doesn't allow it. As pointed out by #Sweeper, Java is broken in this respect and might let you get away with a warning.
This is different when using a generic class or interface. If you define the type parameter at the class/interface level, then the value of that type parameter is determined at construction time of the class, or can be fixed by subclasses. For a given instance, all calls to the method will return a well known type, which is (I believe) what you want here.
You can do this in Java:
public interface Persistable<T extends Statement<T>> {
List<Statement<T>> statements();
}
And then in Kotlin:
data class UpdateRule(
private val something: S
) : Persistable<BoundStatement> {
override fun statements(): List<BoundStatement> {
return PutKeyValue(Function(orgId, serviceId), JsonUtil.toJson(rule)).statements() //this returns BoundStatement
}
}
I am working on a library that has a few interfaces that are related to each other.
Main example is ViewModel<M: Model, W: Widget>. Then we have an interface for Model and interface for Widget.
The widget interface has a method that takes in a ViewModel object and then uses it to configure itself.
abstract fun configure(viewModel: ViewModel<M: Model, W: Widget>)
I am looking for a way to make that method require the subclass implementation of ViewModel, instead of just the interface.
So a ContainerWidget would need this method: fun configure(viewModel: ContainerViewModel)
The library I am emulating is using Swift and does this with their associated type keyword, which says you can tell us the type later.
Does anyone know an option like that for Kotlin?
Thanks a lot for the help.
abstract class WLViewModel<DataModel : WLDataModel, WidgetType : WLWidget>(val dataModel: DataModel) {
abstract fun configure(view: WidgetType)
}
interface WLWidget {
var viewModel: WLViewModel<WLDataModel, WLWidget>
fun <M, O : WLDataModel, T : WLWidget> configure(viewModel: M) where M : WLViewModel<O, T>
}
interface WLDataModel
fun WLWidget.create(viewModel: WLViewModel<WLDataModel, WLWidget>) {
configure(viewModel)
}
I don't think there's a good way, I'm afraid.
The traditional Java approach fudges it by using recursive type parameters. You could give your WLWidget interface a type parameter of the actual subclass, like WLWidget<T : WLWidget<T>>, and then make all the knock-on changes. But that's ugly and doesn't handle all cases. (See these other questions for more info.)
There have been discussions about adding self types to Kotlin, which would probably be a better match for your case, but they're not here yet.
i have a question about kotlin:
Imagine you have this:
sealed class Graph : Serializable
data class Graph1() : Graph() {}
data class Graph2() : Graph() {}
And you want to have a factory class that given a subtype of Graph gives you a GraphView.
So, you have something similar to
interface GraphViewFactory{
fun get(data: Graph1):GraphView
fun get(data: Graph2):GraphView
}
And also you have the implementation for that.
Is possible in kotlin avoid this method explosion of interface having one per graphtype using inline and reified? I'm trying to but i'm not being able.
On the one hand, kotlin interface (I think) does not allow inline functions, on the other hand even without the interface i'm not able to auto cast parameter T as reified to one of the specific subtype class inside the factory class.
You wouldn't have to keep creating methods (though you may want to depending on how complex it is to create a GraphView), but the number of cases in your when will grow.
class GraphViewFactory {
fun get(data: Graph): GraphView {
return when {
is Graph1 -> TODO()
is Graph2 -> TODO()
else -> IllegalArgumentException()
}
}
}
Using reified types doesn't buy you anything here.
I really like using default implementations for interfaces in Kotlin, especially for common patterns like Observable. Here's my interface,
interface Observable<T>{
// How do I cache this?
val observers: MutableList<Observer<T>>
get() = LinkedList<>()
fun addObserver(o:Observer<T>){
observers.add(o)
}
fun removeObserver(o:Observer<T>){
observers.remove(o)
}
fun notifyObservers(u:T){
for (o in observers){
o.update(u)
}
}
}
The interface refers to a list of observers, but the get() call is returning a new LinkedList() each time. How can I cache the value of observers so that it's only created once? I've tried using kotlin-lazy, but either can't get the syntax right, or it's not meant for interfaces. My IDE complains "Delegated properties are not allowed in interfaces."
UPDATE
Based on Yoav's answer, I've change my interface to
interface Observable<T>{
val observers: MutableList<Observer<T>>
}
And then in the implementing class,
class MyObservable : Observable<String>
private val _observers = LinkedList<Observer<String>>()
override val observers: MutableList<Observer<String>>
get() = _observers
Any tips to make this more succinct?
According to Kotlin docs:
Interfaces in Kotlin are very similar to Java 8. They can contain
declarations of abstract methods, as well as method implementations.
What makes them different from abstract classes is that interfaces
cannot store state.
Interface can't hold any state as they are fully abstract. Perhaps you should use an abstract class in order to cache the values?
See this question for more information about the reason for interfaces being stateless.
An interface is a contract specifying what its implementer promises to
be able to do. It does not need to specify state because state is an
implementation detail and only serves to constrain implementers in how
this contract is fulfilled. If you want to specify state you might
want to rethink you use of interfaces and look at abstract base
classes instead.
While you cannot create stateful variables on an interface directly, you can achieve the same result by defining a companion object containing a cache and do a dynamic lookup on this. Using your example:
interface Observable<T>{
// How do I cache this?
val observers: MutableList<Observer<T>>
get() = cache.computeIfAbsent(this) { mutableListOf<Observer<T>>() } as MutableList<Observer<T>>
fun addObserver(o:Observer<T>){
observers.add(o)
}
fun removeObserver(o:Observer<T>){
observers.remove(o)
}
fun notifyObservers(u:T){
for (o in observers){
o.update(u)
}
}
companion object {
val cache = WeakHashMap<Observable<*>, MutableList<*>>()
}
}
If you want a more efficient cache, you can use a library like Caffeine.
After thinking about this some more, I discovered a nice pattern. First I tried the direct approach:
interface A {
val prop = compute() // Nope: Property initializers are not allowed in interfaces
/*...*/
}
As you mentioned, it can be done using a custom accessor but must be recomputed each time:
interface A {
val prop get() = compute() // OK, but recomputed each time someProperty is read
/*...*/
}
Then I thought, perhaps we can use a delegated property instead? But this is not allowed either:
interface A {
val prop by { compute() } // Not allowed: Delegated properties are not allowed in interfaces
/*...*/
}
Finally, I found you can achieve the same result by creating a property extension with a delegate:
interface A { /*...*/ }
val A.prop by lazy { compute() }
From a usage standpoint, you just import the extension and can access a.prop as usual.
I need to create some extension methods in my Java code. I've read some posts here in SO and people suggest XTend or Scala in order to achieve this.
Now, my question would be.. if i write kind of an Adapter layer in Scala (adding there my extension methods) and then using that project as a dependency for my own Java project, are those extended methods available for me to use, or they are defined just for the 'scope of Scala project' and then the JVM output cannot provide those new methods to the other project using it?
EDIT:
What i need to do is to extend a full hierarchy of classes in a given library and give some new functionality. As for Java's first approach I should extend every class in that hierarchy creating my own hierarchy of extended classes adding the new method there. I would like to avoid this and give the final user the sense of native functionality in the original hierarchy.
Regards.
As mentioned above in the comments, it is very close to C# but not exactly there because of the type erasure. For example, this works fine:
object myLibExtensions {
implicit class TypeXExtension( val obj: TypeX ) extends AnyRef {
def myCustomFunction( a: String ): String = {
obj.someMethod(a)
}
}
}
It will act somewhat similar to C# extension methods, i.e. create static method wrappers in reasonable cases (but not always).
The only thing I am missing in Scala is that you can't (or at least I couldn't figure out how to) return the values of the types being extended. For example, assume I want to have something like an extension method "withMeta" that works as follows:
class TypeY extends TypeX { def methodOfY(...) ...}
var y: TypeY = ....
y.withMeta(...).methodOfY(...)
The following didn't work for me:
object myLibExtensions {
private val something = ....
implicit class Extension[T<:TypeX]( val obj: T ) extends AnyRef {
def withMeta( meta: Meta[T] ): T = {
something.associateMeta(obj,meta)
val
}
}
}
... because T is being erased to TypeX. So effectively you will have to write extensions for all specific leaf classes of the hierarchy in this case, which is sad.