I am writing a class that contains two immutable values, which are set in the primary constructor. I would like to add a secondary constructor that takes a string and parses it to get those two values. However, I can't figure out a way to implement this in Kotlin, as the secondary constructor calls the primary constructor immediately, before parsing the string.
In java, I would call this(a,b) in one of the other constructors, but Java doesn't have primary constructors. How do I add this functionality?
class Object (a: double, b:double)
{
val a = a
val b = b
constructor(str: String) //Parsing constructor
{
//Do parsing
a = parsed_a
b = parsed_b
}
}
You can either replace your parsing constructor with a factory method:
class Object(val a: Double, val b: Double) {
companion object {
// this method invocation looks like constructor invocation
operator fun invoke(str: String): Object {
// do parsing
return Object(parsed_a, parsed_b)
}
}
}
Or make both constructors secondary:
class Object {
val a: Double
val b: Double
constructor(a: Double, b: Double) {
this.a = a
this.b = b
}
// parsing constructor
constructor(str: String) {
// do parsing
a = parsed_a
b = parsed_b
}
}
Secondary constructors are disfavored in Kotlin. Your best solution is to use a factory method. See, e.g.:
class A(val a: Int, val b: Int) {
companion object {
fun fromString(str: String): A {
val (foo, bar) = Pair(1, 2) // sub with your parsing stuff
return A(foo, bar)
}
}
}
This will lead to more readable code. Imagine a class with ten different constructors identified no way other than MyClass as opposed to many more obvious ones enabled by the factory approach: MyClass.fromString(str: String) and MyClass.fromCoordinates(coordinates: Pair<Int, Int>) and so forth.
Secondary constructors weren't even allowed in Kotlin until relatively recently.
Related
I have a list of clases that implement a specific interface. The ability to construct those clases or not is not static (so it's not possible to use when(className)), and can be configured so I want to be able to create some clases or call some methods based on a hashMap of allowed "constructors". Then if the key identifying a class is in present in the hashmap I can call the corresponding method, otherwise I can safely ignore. Let me illustrate:
Let's say I have an interface like
interface Instanceable {
data class Config(
val bar: Whatever
)
fun getIntance(config: Config): Instanceable
}
Then I have several (let's say 10) classes that implement this interface
class Implementation1() : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private lateinit var foo: Whatever
override fun getIntance(config: Config) = Implementation1().also{ this#Implementation1.foo = config.bar }
}
I want to create a hashmap of the methods by the identifiers, so later down the lane I can grab the method from the hashMap by the key ID and just invoke() the value if it's there. Something like:
allowedInstances("INSTANCE_1")?.let{ it.invoke(someConfig) }
In order to do this I tried to create a hashMap of methods like this:
private val allowedInstances = mutableHashMapOf<String, Instanceable.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
But it fails with:
Type mismatch.
Required: Instanceable.(Instanceable.Config) → Instanceable
Found: KFunction2<Implementation1, Instanceable.Config, Instanceable>
If I create the hashmap directly and let the compiler infer the types like this:
private val allowedInstances = mutableHashMapOf(
Implementation1.ID to Implementation1::getIntance,
Implementation2.ID to Implementation2::getIntance,
Implementation4.ID to Implementation4::getIntance,
)
Checking the type of the hashmap shows:
HashMap<String, out KFunction2<Nothing, Instanceable.Config, Instanceable>>
In fact I can do:
private val allowedInstances = mutableHashMapOf<String, Nothing.(Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = Instance1::getIntance
allowedInstances[Instance2.ID] = Instance2::getIntance
allowedInstances[Instance4.ID] = Instance4::getIntance
So the actual question is:
Why the function of the second hashMap parameter has Nothing as the receptor? Why I cannot have the interface Instanceable instead?
Edit: Still not good to have the Nothing there:
allowedInstances["INSTANCE_1"]?.let{ it.invoke(Nothing, someConfig) }
//Fails with: Classifier 'Nothing' does not have a companion object, and thus must be initialized here
Edit 2: All of the errors are in compile time
Your function type
Instanceable.(Instanceable.Config) -> Instanceable
is describing an extension function on an instance of Instanceable. You need to omit the receiver from the function type to be able to match your constructors' signature:
(Instanceable.Config) -> Instanceable
Edit: The other half of the problem is that you define getInstance() as a member function of the class. So you have to create an invalid instance of your class to use to create a valid instance, which doesn't make sense.
I would delete the getInstance() function from your interface, and put the equivalent code in the constructor of your class. Then you can define a function type in your Map that constructs your items.
interface Instanceable {
data class Config(
val bar: Whatever
)
// REMOVE this: fun getIntance(config: Config): Instanceable
}
class Implementation1(config: Config) : Instanceable {
companion object {
const val ID = "INSTANCE_1"
}
private val foo: Whatever = config.bar
}
private val allowedInstances = mutableHashMapOf<String, (Instanceable.Config)->Instanceable>()
allowedInstances[Instance1.ID] = ::Implementation1
// and so on...
// If there's an implementation that has no config, you can use a lambda:
class NoConfigImplementation : Instanceable {
companion object {
const val ID = "INSTANCE_2"
}
}
allowedInstances[NoConfigImplementation.ID] = { _ -> NoConfigImplementation() }
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)
}
}
Following similar patterns in other languages, I would be interested in producing the most useful way to strongly-type a primitive type in Kotlin.
The rationale, of course, is to have two types which are basically primitive (e.g. strings), but which cannot be assignable to each other by mistake.
My latest attempt is given here, and I'm interested to know how can it be minimized further (can defining the derived constructor be omitted?)
abstract class StronglyTyped<T>{
private var value: T
constructor(_value: T) {
value = _value
}
operator fun invoke(): T {
return value
}
}
class UserId: StronglyTyped<String> {
constructor(_value: String): super(_value) {}
}
class UserName: StronglyTyped<String> {
constructor(_value: String): super(_value) {}
}
fun main() {
val a = UserId("this is a userId")
val b = UserName("this is a userName")
var c: UserName
//c = a // <== won't compile
c = b
println(c())
}
Sounds like you're looking for value classes. More information is available in the official documentation.
An example might look something like the following:
value class Password(val value: String)
If you want to enforce some validation on the primitive, you can do so inside the init block.
value class UserId(val value: String) {
init {
require(value.length == 8) { "A userId must be exactly 8 characters long!" }
}
}
Note however, that this just provides compile-time type safety, because the original primitive types are used during the runtime.
In Kotlin, if I have a function that calls a constructor (or another function) is there a way to delegate the determination of default argument to the underlying constructor/function?
This syntax isn't valid, but hopefully this illustrates what I'm trying to do:
fun buildMyObject(stringParam: String?, intParam: Int?): MyClass {
return MyClass(stringParam, intParam)
}
class MyClass(val stringParam: String = "Hello world!", val intParam: Int = 42)
There's no support for exactly what you want. However, if you have a bit of flexibility about where buildMyObject lives, you can have something like:
class MyClass(val stringParam: String = defaultString, val intParam: Int = defaultInt) {
private constructor(stringParam: String?, intParam: Int?) : this(
stringParam ?: defaultString,
intParam ?: defaultInt
)
companion object {
private const val defaultString = "Hello world!"
private const val defaultInt = 42
fun buildMyObject(stringParam: String?, intParam: Int?): MyClass {
return MyClass(stringParam, intParam)
}
}
}
This has a couple of nice characteristics:
Lets users still call the null-safe MyClass constructor
Doesn't duplicate where the default values are defined
Doesn't grow exponentially in the number of constructors you'd need if you'd try to use a when and call with or without each param (imagine what would happen if you'd add a third parameter!)
Keeps the nullable constructor private, so callers can only use nullables if they go through the factory method
I tried this, and the code didn't compile.
class GenericClass<T>() {
private var arr : Array<T>? = null
{
arr = Array<T>(10, { null })
}
}
There are two compiler errors reported in this code: one is about nullable types and another about generics.
Nullable types. Kotlin enforces a discipline of nullable references, and since T may be instantiated with, say, String making arr be of type Array, the compiler does not allow you to put nulls into this array. If you want nulls, you have to change the type to Array:
class GenericClass<T>() {
private var arr : Array<T?>? = null
{
arr = Array(10, { null }) // No need to specify type arguments again
}
}
Generics. The example above still has a compile-time error, because we are trying to construct an array of an unknown type T. Note that this problem exists in Java as well. Kotlin being compiled to JVM byte code entails two things:
generics type arguments are erased at runtime,
except for generic arguments of arrays.
This means that in the byte code Kotlin has to create an array of some concrete type, and not an unknown type T. It could create arrays of Objects whenever it sees Array, but this would not work, for example, in this case:
fun test() {
fun foo(srts: Array<String?>) {
// ...
}
val gc = GenericClass<String>()
foo(gc.arr)
}
Here, in the last line, we are trying to pass Object[] where String[] is expected, and get a runtime error.
This is why Kotlin refuses to create arrays of T. You can work around this problem by explicitly suppressing the type system, i.e. by using type casts:
class GenericClass<T>() {
val arr : Array<T?>
{
arr = Array<Any?>(10, { null }) as Array<T?>
}
}
Here we explicitly request creation of an array of Any (compiled to Object[]), and then type-cast it to an array of T. The compiler issues a warning, but obeys our will.
Note that the problematic example above remains, i.e. if you pass the array created this way where an array of strings is expected, it ill fail at run time.
method
val array : Array<T?> = kotlin.arrayOfNulls<T>(size)
from docs
/**
*Returns an array of objects of the given type with the given [size],
*initialized with null values.
*/
public fun <reified #PureReifiable T> arrayOfNulls(size: Int): Array<T?>
If you need to initialize array in the constructor, you can add an inline factory method and parametrize it using reified T. This solution is inspired by answer https://stackoverflow.com/a/41946516/13044086
class GenericClass<T> protected constructor(
private val arr : Array<T?>
) {
companion object {
inline fun <reified T>create(size: Int) = GenericClass<T>(arrayOfNulls(size))
}
}
fun main() {
val strs = GenericClass.create<String>(10)
...
}
Notice that the constructor is protected, because inline function can't access a private constructor.
If you need to create an array after the object is created, you can pass lambda that creates the array into the method. Lambda can be created inside of extension function, so information about type of the array is preserved. #PublishedApi annotation is used to encapsulate private method fill.
import GenericClass.Companion.fill
class GenericClass<T> {
private var arr : Array<T?>? = null
fun show() {
print(arr?.contentToString())
}
private fun fill(arrayFactory: (size: Int) -> Array<T?>) {
this.arr = arrayFactory(10)
}
#PublishedApi
internal fun `access$fill`(arrayFactory: (size: Int) -> Array<T?>) = fill(arrayFactory)
companion object {
inline fun <reified T>GenericClass<T>.fill() {
`access$fill`(arrayFactory = { size -> arrayOfNulls(size) })
}
}
}
fun main() {
val strs = GenericClass<String>()
strs.fill()
strs.show()
}
You could use a helper function as below:
#Suppress("UNCHECKED_CAST")
fun <T> genericArrayOfNulls(size: Int): Array<T?> {
return arrayOfNulls<Any?>(size) as Array<T?>
}