How can generics in functions be used to avoid duplicate code? - kotlin

I have a specific question about the usage of generics in Kotlin.
I want to create a function which takes a generic T as an argument.
It uses that to assign name from one of the classes: Class1 or Class2 to the local variable testString.
Unfortunately this is only possible when I check the type of the argument with the if conditions.
This leads to duplicate code. If I try to avoid that and use Line 12 I get this error during compile time: Unresolved reference: name
Is it possible in Kotlin to avoid the if conditions and use the testString assignment only once when the classes you are going to use have the same property with the same name?
Code:
fun main() {
val class1 = Class1("Foo1")
val class2 = Class2("Foo2")
}
class Class1(val name: String)
class Class2(val name: String)
fun <T> doStuff(classOneOrTwo: T) {
var testString: String
testString = classOneOrTwo.name //not working: Unresolved reference: name
if (classOneOrTwo is Class1) {
testString = classOneOrTwo.name
}
if (classOneOrTwo is Class2) {
testString = classOneOrTwo.name
}
}

You don't need generics here.
You can just write an interface that requires its implementers to have a name property.
interface HasName {
val name: String
}
Class1 and Class2 should implement the interface:
class Class1(override val name: String): HasName
class Class2(override val name: String): HasName
Then doStuff can be written as:
fun doStuff(classOneOrTwo: HasName) {
var testString = classOneOrTwo.name
// ...
}
You can make doStuff generic:
fun <T: HasName> doStuff(classOneOrTwo: T) {
var testString = classOneOrTwo.name
// ...
}
But you don't gain anything in particular by doing so.
Non-reified* generics are the most helpful when you want to establish some kind of "link", whether it be between parameters, or between parameters and the return type. For example, if your method is supposed to return the same type of thing as it takes:
fun <T> doStuff(foo: T): T { ... }
Or your method takes two parameters, and the second parameter must be the element type of the first parameter, which is a mutable list:
fun <T> doStuff(list: MutableList<T>, t: T) { ... }
* This paragraph doesn't quite apply to reified generics, which could be useful on their own.

Class1 and Class2 have nothing in common for the doStuff function to resolve the property name even though they were written exactly the same way, if you expect that just because you have a generic parameter T everything will be automatically be resolved, unfortunately the compiler doesn't know what T is here, aside from it being implcitly Any? type, (i.e <T: Any?>).
You're having a compile error here becase name is not a property of Any?
classOneOrTwo.name //not working: Unresolved reference: name
However, calling the doStuff function compiles fine because everything in Kotlin is a direct or indirect child of Any?
fun main() {
val class1 = Class1("Foo1")
val class2 = Class2("Foo2")
doStuff(class1)
doStuff(class2)
}
and if you try to invoke some function using classOneOrTwo param and pressed cltr+click on it, youll see its a function of the type Any?
fun <T> doStuff(classOneOrTwo: T) {
...
...
classOneOrTwo.toString() // <-- ctrl + click this you'll see its a function of Any?,
You should create a hierarchy (Inheritance) where Class1 and Class2 can inherit something from, in your case name
open class ParentClass(open val name: String)
class Class1(override val name: String) : ParentClass(name)
class Class2(override val name: String) : ParentClass(name)
fun <T: ParentClass> doStuff(classOneOrTwo: T) {
Log.e("DoStuff", classOneOrTwo.name) // now this works find because
}
Now it works because you explicitly tell the compiler that T is a type of ParentClass where the name property exists, and is inherited by your Class1 and Class2.
Back to your main function
doStuff(class1)
doStuff(class2)
prints,
Foo1
Foo2

Related

Cloning object of subclass type in Kotlin

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

Is it possible to overload function with receiver operator in Kotlin?

I can define invoke inside a class
class A {
fun invoke(x: Double): Double {
...
}
}
and then use class instance as a functiion
val a: A()
val b = a(2.3)
right?
But can I define class instance to simulate function with receiver?
val o: MyClass()
val a: A()
val b = o.a(2.3)
Is it possible?
and then use class instance as a functiion
The invoke operator is just a way to define what happens when using the syntax () on some instance. Just like you can overload what + means, you can overload what () means. It's not exactly making an instance of A "usable as a function", but rather defining the operator () on instances of A. This is why I think it cannot really translate to "making it usable as a function with receiver".
The obvious easy way to declare an extension function would be the following:
fun MyClass.a(input: Double): Double = TODO(...)
But this doesn't seem to suit your needs. If what you really want is to add such functions as "capabilities" to some instances dynamically "on the spot" as in your example, I guess you could do so by defining such extension in a class that you provide as scope:
class A {
fun MyClass.a(x: Double): Double {
...
}
}
fun main() {
val o = MyClass()
val b = with(A()) { // brings this instance of A in scope to add the extension
o.a(2.3)
}
}

Delegation to another object of same type using by does not even compile

I am trying to understand how the delegate keyword by works.
So delegating to implemenent an interface is clear e.g.
class Manager(clientele: List<Client> = ArrayList()): List<Client> by clientale
But the following does not work:
data class Client(val name: String, val postalCode: Int)
fun createClient() = Client("Bob", 1234)
val bigClient: Client by createClient() // compilation error
I get the error:
Missing getValue(Nothing?, KProperty<*>) method delegate of type
Client
I thought that if two objects are the same the delegation from one to the other (Client by Client) would work.
Can someone please explain what is the error here and what am I doing wrong?
Unfortunately that's not exactly how delegation of properties works. Based on the documentation:
For a read-only property (i.e. a val), a delegate has to provide a function named getValue that takes the following parameters:
thisRef - must be the same or a supertype of the property owner;
property - must be of type KProperty<*> or its supertype.
For a mutable property (a var), a delegate has to additionally provide a function named setValue that takes the following parameters:
thisRef - same as for getValue();
property - same as for getValue();
newValue - must be of the same type as the property or its subtype.
[...] Both of the functions need to be marked with the operator keyword.
So in order just to make your example work, you have to add a getValue() method which meets the above requirements:
data class Client(val name: String, val postalCode: Int) {
operator fun getValue(thisRef: Nothing?, property: KProperty<*>): Client = this
}
You can also use and implement the ReadOnlyProperty and ReadWriteProperty interfaces which provide the required methods:
data class Client(val name: String, val postalCode: Int) : ReadOnlyProperty<Nothing?, Client> {
override fun getValue(thisRef: Nothing?, property: KProperty<*>): Client = this
}
Edit:
What is this getValue() supposed to do?
Let me explain a little further on a more abstract example. We have the following classes:
class MyDelegate : ReadWriteProperty<MyClass, String> {
private var delegateProperty: String = ""
override fun getValue(thisRef: MyClass, property: KProperty<*>): String {
println("$thisRef delegated getting the ${property.name}'s value to $this")
return delegateProperty
}
override fun setValue(thisRef: MyClass, property: KProperty<*>, value: String) {
println("$thisRef delegated setting the ${property.name}'s value to $this, new value: $value")
delegateProperty = value
}
}
class MyClass {
var property: String by MyDelegate()
}
The above MyClass would get compiled more or less to:
class MyClass {
private var property$delegate: MyDelegate = MyDelegate()
var property: String
get() = property$delegate.getValue(this, this::property)
set(value) = property$delegate.setValue(this, this::property, value)
}
So you can see that the compiler requires a delegate to have getValue() and setValue() methods for mutable properties (var) or only getValue() for immutable properites (val), because it uses them to respectively get and set the delegated property's value.
What are Nothing and KProperty<*>?
KProperty<*> is a Kotlin class which represents a property and provides its metadata.
Nothing is a type that represents a value that doesn't exist. It's quite irrelevant from the delegation point of view. It came up in this case, because you probably defined the bigClient property outside any class so it has no owner, hence thisRef is Nothing.

How to define functional interface with generics in Kotlin?

I'm learning Kotlin and I have some trouble with functions.
I'm trying to create something like a functional interface with a generic parameter.
In Java I would create something like this:
#FunctionalInterface
public interface Foo<T extends Bar> {
String something(T arg);
}
Then I can use this somewhere else like this (given that Person extends Bar:
Foo<Person> f = p -> p.toString();
How do you write this with Kotlin?
The first thing I tried was to use type-aliases like this:
typealias Foo<T> = (T) -> String
However, it stopped working when I added the bound to the type parameter:
typealias Foo<T: Bar> = (T) -> String // Error: Bounds are not allowed on type alias parameters
The second approach was to write an interface that extends the function type:
interface Foo<T: Bar> : (T) -> String
However, now I don't know how to instantiate a lambda function from with this. It works when I create class from it like this:
class Something: Foo<Person> {
override fun invoke(p: Person): String {
return p.toString()
}
}
val f = Something()
But this is a big overhead and I'm sure there has to be a better solution.
So how can I define a function signature that can be reused by many functions that supports generic parameters with bounds in kotlin?
Most of the time (always?) it is sufficient to define the type of the lambda in the parameter of the function that receives it.
For example:
open class Bar
class Person: Bar()
var f = { p: Person -> p.toString() }
fun <T : Bar> withFoo(block: (T) -> String) { }
fun <T : Bar> otherFoo(block: (T) -> String) { }
fun main() {
withFoo(f)
otherFoo(f)
}
The same way the Kotlin documentation states:
"since Kotlin has proper function types, automatic conversion of functions into implementations of Kotlin interfaces is unnecessary and therefore unsupported."
See https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions

Extending a T type, delegating to T whatever behavior is need for its class

I want to add some behavior to the T generic type of my class, but I still want my class to exist. I want a class to be both itself and the T type. For instance:
open class Foo(val bar: String)
class Bar(val baz: String) : Foo("bar") {
}
This is an easy case, because I know Foo type in advance. If I make Foo an interface, I can delegate it's methods to a parameter:
interface Foo {
fun bar() = "bar"
}
class Bar(val foo: Foo) : Foo by foo {
}
And Bar is still a Foo.
But what if I don't know what type it is at this time? I want Bar to be T aswell as Bar, and I thought of something like this:
class Bar<T>(val t: T) : T by t
But it doesn't compile. I wanted to use it in this fashion:
fun doWithFoo(s: String) {
print(s)
}
fun unknownFoo() {
val bar = Bar("baz")
doWithFoo(bar)
}
This might be a bizarre use case, but it's necessary. I need it to capture arguments passed to a function, and make assertions over it, but I need the function to still be valid, so:
fun foo(b: Bar){
print(b.toString())
}
If I want to create an argument capturer for the function foo, I could create something that captures it
class Capturer<T>(t: T) {
//code that captures the value and make assertions over it
}
But then the function would become invalid:
val capturer = Capturer<Bar>(Bar("X"))
foo(capturer) //Invalid
So I need capturer to also be a Bar. This way the function foo is still valid.
How could I make the Bar class to be both a Bar and the generic type T?
I don't think that I really got what you are trying to accomplish.
But maybe the following may help you solve your problem?
If you use
fun <T> foo(b: T) {
//...
and
fun <T> doWithFoo(s: T) {
//...
at least the call will not be invalid anymore.