Generics question. I am trying to make a construct...
# getting-started
a
Generics question. I am trying to make a constructor where one value is accepted as
T: Number
and another value in the class is initialized as 0 but that type of Number. How do I do that?
Copy code
class Complex<T: Number> {
  var real: T
  var imaginary: T

  constructor (real: T) {
    this.real = real
    imaginary = 0.ofType(T) // this is where help is needed
  }

  constructor (real: T, imaginary: T) {
    this.real = real
    this.imaginary = imaginary
  }
}
e
I don’t think there’s a generic way to do this without passing the actual instance. So I’d rely on
constructor(real: T, imaginary: T)
and call it with the corresponding zero value.
a
This can't be easily done. But if you really want to, you can use something like this:
Copy code
class Complex<T : Number>(var real: T, var imaginary: T)

@Suppress("FunctionName", "UNCHECKED_CAST")
fun <T : Number> Complex(real: T): Complex<T> =
    when (real) {
        is Int -> Complex(real, 0 as T)
        is Long -> Complex(real, 0L as T)
        is Float -> Complex(real, 0f as T)
        is Double -> Complex(real, 0.0 as T)
        // Other types
        else -> throw IllegalArgumentException()
    }
g
Hello @alex cole. You could try something like this:
Copy code
constructor (real: T) {                                         this.real = real                  imaginary = 0 as T                  }
a
@Glen That doesnt seem to work wither. It throws a
warning: unchecked cast: Int to T
e
@alex cole I believe that the key issue here is that
Number
does not give you that kind of abstraction that you are looking for. In your case, ideally
Number
would provide an abstract method for
ZERO
, but in Kotlin, it only provides you a set of conversion methods. With that in mind, you still have a few options to consider: a. tying your structure to a concrete number implementation (e.g.
Double
) b. implementing your own
Number
abstraction and wrap all the number types to that (I don’t recommend this one) c. keep going with the existing
Number
class and deal with the unchecked cast warning.
g
Yeah, I agree with @Endre Deak...
a
So what would be the best way then to make a complex number such as ComlexDouble that could interact with both real and complex values?
a
@Endre Deak That abstraction won't work. Even if you have a
Number.ZERO
abstract val it returns
Number
instead of the specific child type (
T : Number
) so you have to use unchecked cast anyway.
a
Would I use real -> is or should I use real::class?
a
It throws a warning.
Copy code
> kotlinc -d main.jar main.kt
main.kt:15:19: warning: unchecked cast: Int to T
    imaginary = 0 as T
                  ^
> kotlin -classpath main.jar MainKt
Success
0.0 + 0i
>
g
I see. But the program executes and completes successfully...
d
How do you feel about reflection?
this.imaginary = real::class.cast(0)
Also maybe noteworthy there’s no need to define two constructors:
class Complex<T : Number>(var real: T, var imaginary: T = real::class.cast(0))
providing the default second argument allows the caller to provide either just real or both arguments
As an aside, T : Number also includes Number so you can invoke this like Complex(0.0, 0L) and the type would be Complex<Number> because the double and the long only have one possible type T in common.
a
@Dave K
kotlin.reflect.KClasses.cast
does not support conversion between numeral types. Try running
println(0f::class.cast(0))
and you'll get a
ClassCastException
.
d
Riiight my bad
Assuming you wouldn’t want imaginary and real to be different types, I think you’d need to make Complex an interface and implement some concrete classes for each expected type, and then you can have a function that acts as a factory. https://pl.kotl.in/p3Qqte-_7