I've the following two classes:
class Volume(var value: Double, unit: Unit) {
var unit: Unit = unit
private set
enum class Unit(symbol: String){
MILLILITER("ml"),
CENTILITER("cl"),
DECILITER("dl"),
LITER("l"),
TEASPOON("tsp"),
TABLESPOON("tbsp"),
FLUIDOUNCE("floz"),
SHOT("jig"),
GILL("gi"),
CUP("cup"),
PINT("pt"),
QUART("qt"),
GALLON("gal")
}
}
class Mass(var value: Double, unit: Unit) {
var unit: Unit = unit
private set
enum class Unit(symbol: String){
GRAM("g"),
DECAGRAM("dag"),
HECTOGRAM("hg"),
KILOGRAM("kg"),
OUNCE("oz"),
POUND("lb")
}
}
I want to create operators for both classes for basic arithmetic operations, for example:
operator fun inc(): Mass {
value++
return this
}
Since both classes will have the same operator logics, i don't want to duplicate this part of the code.
My first idea was that Both classes inherit from a PhysicalQuantity interface which contains the operators. In this case the following code doesn't work, because the IDE expects IPhysicalQuantity as return type but the type is Volume:
interface IPhysicalQuantity() {
var value: Double
var unit: IUnit
operator fun inc(): IPhysicalQuantity {
value++
return this
}
}
fun main() {
var vol = Volume(10.0, Volume.Unit.CENTILITER)
vol++
}
Same issue with abstract super class.
The problem with doing this inside the IPhysicalQuantity interface is that you don't want to return the object as the interface type IPhysicalQuantity from the inc method. Instead, you want to keep its original type (Volume or Mass), so you'd have to use generics there. However, I didn't find a way to do this without complex syntax and an unchecked cast:
interface IPhysicalQuantity<T : IPhysicalQuantity<T>> {
var value: Double
operator fun inc(): T {
value++
return this as T
}
}
class Volume(override var value: Double, unit: Unit): IPhysicalQuantity<Volume>
However, you can do this fairly simply with an extension instead without having to make the interface itself generic, if that works for you:
operator fun <T : IPhysicalQuantity> T.inc(): T {
value++
return this
}
Related
In Kotlin, can a property of a data class have multiple types? For example:
val CurrentValue: Double?|String
or
val CurrentValue: String|Array?
I cannot find it in the documentation.
Union types are not a thing in Kotlin.
You may use a sealed class instead.
sealed class CurrentValue<T>(val value: T) {
class TextualValue(value: String) : CurrentValue<String>(value)
class NumericValue(value: Double) : CurrentValue<Double>(value)
}
Which then you can use exhaustive when expressions (similar to switch in other languages) in order to access the value in a type-safe manner:
fun doSomething(value: CurrentValue<*>) {
when(value) {
is TextualValue -> value.value // is recognised as a String
is NumericValue -> value.value // is recognised as a Double
}
}
If creating a type is way too much for you then you can perform a when statement and treat a parameter based on it's type and perhaps normalize it:
fun parseValue(value: Any?): Double? = when(value){
is Double -> value
is String -> value.toDoubleOrNull()
is Int -> value.toDouble()
else -> null
}
I'm trying to write serialization functions to be able to serialize any vector (=ArrayList) in Kotlin, as well as primitive types and classes extending a Serialize class having a toBinary() function.
I also have a custom WriteDataStream class (code below) to serialize fields with the right format, endianness, etc.
I'm new to Kotlin but have experience in C++. In C++, I used templates and template specialization to solve that problem easily, but with Kotlin I've been struggling for a few days, without success.
I have a custom vector class MyVector which extends ArrayList and adds a maximum size. I want to serialize it with any generic type T, including inner vectors like a MyVector<MyVector<MyClass>>.
My WriteDataStream contains the following:
inline fun <reified T> write(vector: MyVector<T>) {
this.writeSize(vector.size.toULong(), vector.MAX_SIZE)
for (element in vector) {
write<T>(element)
}
}
inline fun <reified T: Serialize> write(value: T) {
writeSerialize(value as Serialize)
}
inline fun <reified T> write(value: T) {
when (T::class) {
UByte::class -> {
writeUInt8(value as UByte)
}
UShort::class -> {
writeUInt16(value as UShort)
}
UInt::class -> {
writeUInt32(value as UInt)
}
ULong::class -> {
writeUInt64(value as ULong)
}
Byte::class -> {
writeInt8(value as Byte)
}
Short::class -> {
writeInt16(value as Short)
}
Int::class -> {
writeInt32(value as Int)
}
Long::class -> {
writeInt64(value as Long)
}
Boolean::class -> {
writeBoolean(value as Boolean)
}
Float::class -> {
writeFloat(value as Float)
}
Double::class -> {
writeDouble(value as Double)
}
else -> {
error("Default serialization:" + T::class.qualifiedName)
}
}
}
All the underlying functions writeXXX() are tested and work fine. However, when tying to serialize a MyVector with a class extending Serialize, I fall in the "Default serialization" case:
#Test
fun writeVectorOfStructure() {
class TestStructure: Serialize() {
override fun toBinary(stream: WriteDataStream) {
stream.writeUInt32(17U)
stream.writeUInt8(3U)
stream.writeDouble(555.555)
}
}
val value = MyVector<TestStructure>(MAX_SIZE, arrayListOf(TestStructure(), TestStructure()))
writeStream.write(value)
val bytes: UByteArray = writeStream.byteArray()
Assert.assertEquals(bytes.size, 28) // = 2 (for size) + 2*(4+1+8) = 28 bytes
}
So my question is: Why does Kotlin not use the function
inline fun <reified T: Serialize> write(value: T)
when it serializes an element of the vector (write<T>(element)) with generic T = Serialize, but instead uses the more generic one?
inline fun <reified T> write(value: T)
In C++, the compiler always uses the most fitted function.
Is there a way to overcome this limitation in Kotlin?
I have tried with and without reified types, I have tried a non-generic function as well: inline fun write(value: Serialize), but without success. The only thing that seems to work was to add a case for classes "instance of" Serialize in the fully-generic inline fun <reified T> write(value: T), but this is not really a nice solution.
Thanks you !
JVM and its bad implementation of generics
You are a victim of Java's implementation of generics, more specifically the erasure. C++ uses what is called type expansion to implement generics, meaning if you declare MyType<A> and MyType<B>, at runtime you will have two different types, language runtime will create them for you.
On the other hand what Java does is called the erasure implementation. so in java world when you say List<String> and List<Integer>, at runtime they are both identical types, that is system doesn't have any information to make a distinction between both of these, they are List type (Note that there is no type parameter, it got removed during the compilation).
Lets decompile your code and see for yourself
I wrote following code in kotlin, it matches yours
class SomeType {
inline fun <reified T: String> write(value: T) {}
inline fun <reified T> write(value: T) {}
inline fun <reified T: Any> write(vector: List<T>) {
for (element in vector) {
write(element)
}
}
}
And when I decompile the code it gives me following. (Only relevant code included)
public final class SomeType {
public final void write(#NotNull String value) {}
public final void write(Object value) {}
public final void write(#NotNull List vector) {
boolean var6;
for(Iterator var4 = vector.iterator(); var4.hasNext(); var6 = false) {
Object element = var4.next();
}
}
}
Look at the write(vector: List<T>) method's decompilation. parameter type got changed to List which is a Raw Type and its components are objects.
And for an Object best method match is public final void write(Object value) and not the one with String or in your case Serialize.
Can we implement Rust like Traits and generic Traits using Kotlin Interfaces?
Also is there any way of using fn(&self) like construct in Kotlin class/interface default function implementations?
Can some examples be shown please?
Thanks
I don't know much about Rust, I'm referrring to these two videos as for what you're talking about, generic traits and &self explaination.
In kotlin you'd implement them using interfaces and classes as you've guessed.
An example of that is:
interface GenericTrait { // Same as traits
// <T:Any> just makes method to be called for non-null values, if you use <T>, you can pass null as well
fun <T: Any> method(value: T)
}
class TraitImpl : GenericTrait { // Same as structs
val isDisabled = Random.nextBoolean() // instance variable
// you can access instance parameter using the this or even not using it at all as in below
override fun <T: Any> method(value: T) {
println("Type of value is ${value::class}, and the value is $value. I am $isDisabled")
// or explicitly call ${this.isDisabled}, both are the same
}
}
fun main() {
TraitImpl().method("Hello")
TraitImpl().method(23)
TraitImpl().apply { // this: TraitImpl
method(23)
method(Unit)
}
}
Result:
Type of value is class kotlin.String, and the value is Hello. I am true
Type of value is class kotlin.Int, and the value is 23. I am true
Type of value is class kotlin.Int, and the value is 23. I am false
Type of value is class kotlin.Unit, and the value is kotlin.Unit. I am false
You can extract implementation outside if you want as an extension function just like you do in Rust.
interface GenericTrait {
val isDisabled: Boolean
}
class TraitImpl : GenericTrait {
override val isDisabled = Random.nextBoolean()
}
// define methods out of class declaration
fun <T: Any> GenericTrait.method(value: T) {
println("Type of value is ${value::class}, and the value is $value. I am $isDisabled")
}
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
Is there a way to specify the return type of a function to be the type of the called object?
e.g.
trait Foo {
fun bar(): <??> /* what to put here? */ {
return this
}
}
class FooClassA : Foo {
fun a() {}
}
class FooClassB : Foo {
fun b() {}
}
// this is the desired effect:
val a = FooClassA().bar() // should be of type FooClassA
a.a() // so this would work
val b = FooClassB().bar() // should be of type FooClassB
b.b() // so this would work
In effect, this would be roughly equivalent to instancetype in Objective-C or Self in Swift.
There's no language feature supporting this, but you can always use recursive generics (which is the pattern many libraries use):
// Define a recursive generic parameter Me
trait Foo<Me: Foo<Me>> {
fun bar(): Me {
// Here we have to cast, because the compiler does not know that Me is the same as this class
return this as Me
}
}
// In subclasses, pass itself to the superclass as an argument:
class FooClassA : Foo<FooClassA> {
fun a() {}
}
class FooClassB : Foo<FooClassB> {
fun b() {}
}
You can return something's own type with extension functions.
interface ExampleInterface
// Everything that implements ExampleInterface will have this method.
fun <T : ExampleInterface> T.doSomething(): T {
return this
}
class ClassA : ExampleInterface {
fun classASpecificMethod() {}
}
class ClassB : ExampleInterface {
fun classBSpecificMethod() {}
}
fun example() {
// doSomething() returns ClassA!
ClassA().doSomething().classASpecificMethod()
// doSomething() returns ClassB!
ClassB().doSomething().classBSpecificMethod()
}
You can use an extension method to achieve the "returns same type" effect. Here's a quick example that shows a base type with multiple type parameters and an extension method that takes a function which operates on an instance of said type:
public abstract class BuilderBase<A, B> {}
public fun <B : BuilderBase<*, *>> B.doIt(): B {
// Do something
return this
}
public class MyBuilder : BuilderBase<Int,String>() {}
public fun demo() {
val b : MyBuilder = MyBuilder().doIt()
}
Since extension methods are resolved statically (at least as of M12), you may need to have the extension delegate the actual implementation to its this should you need type-specific behaviors.
Recursive Type Bound
The pattern you have shown in the question is known as recursive type bound in the JVM world. A recursive type is one that includes a function that uses that type itself as a type for its parameter or its return value. In your example, you are using the same type for the return value by saying return this.
Example
Let's understand this with a simple and real example. We'll replace trait from your example with interface because trait is now deprecated in Kotlin. In this example, the interface VitaminSource returns different implementations of the sources of different vitamins.
In the following interface, you can see that its type parameter has itself as an upper bound. This is why it's known as recursive type bound:
VitaminSource.kt
interface VitaminSource<T: VitaminSource<T>> {
fun getSource(): T {
#Suppress("UNCHECKED_CAST")
return this as T
}
}
We suppress the UNCHECKED_CAST warning because the compiler can't possibly know whether we passed the same class name as a type argument.
Then we extend the interface with concrete implementations:
Carrot.kt
class Carrot : VitaminSource<Carrot> {
fun getVitaminA() = println("Vitamin A")
}
Banana.kt
class Banana : VitaminSource<Banana> {
fun getVitaminB() = println("Vitamin B")
}
While extending the classes, you must make sure to pass the same class to the interface otherwise you'll get ClassCastException at runtime:
class Banana : VitaminSource<Banana> // OK
class Banana : VitaminSource<Carrot> // No compiler error but exception at runtime
Test.kt
fun main() {
val carrot = Carrot().getSource()
carrot.getVitaminA()
val banana = Banana().getSource()
banana.getVitaminB()
}
That's it! Hope that helps.
Depending on the exact use case, scope functions can be a good alternative. For the builder pattern apply seems to be most useful because the context object is this and the result of the scope function is this as well.
Consider this example for a builder of List with a specialized builder subclass:
open class ListBuilder<E> {
// Return type does not matter, could also use Unit and not return anything
// But might be good to avoid that to not force users to use scope functions
fun add(element: E): ListBuilder<E> {
...
return this
}
fun buildList(): List<E> {
...
}
}
class EnhancedListBuilder<E>: ListBuilder<E>() {
fun addTwice(element: E): EnhancedListBuilder<E> {
addNTimes(element, 2)
return this
}
fun addNTimes(element: E, times: Int): EnhancedListBuilder<E> {
repeat(times) {
add(element)
}
return this
}
}
// Usage of builder:
val list = EnhancedListBuilder<String>().apply {
add("a") // Note: This would return only ListBuilder
addTwice("b")
addNTimes("c", 3)
}.buildList()
However, this only works if all methods have this as result. If one of the methods actually creates a new instance, then that instance would be discarded.
This is based on this answer to a similar question.
You can do it also via extension functions.
class Foo
fun <T: Foo>T.someFun(): T {
return this
}
Foo().someFun().someFun()