Kotlin: Prevent mixup of same typed parameters - kotlin

How can I save the cats from being served for dinner?
data class Animal(
val name: String
)
data class ToDo(
val toEat: List<Animal>,
val toFeed: List<Animal>,
)
val cats = listOf(Animal("fluffy"))
val chickens = listOf(Animal("chic"))
// help the kittens!!!
ToDo(cats, chickens)
Note: This is a simplified example and I cant change the Type of cats/chickens to something like Cat/Chicken.

OOP solution
You could do the following:
Animal should be an interface.
Cat and Chicken should be two different implementation of Animal.
You could define an Interface named Eatable which will have concrete implementations.
Change val toEat: List<Animal> in val toEat: List<Eatable>
Cat will implement Animal and not Eatable
Another solution
Sorry but i read just now that you cannot define 2 different classes for Cat and Chicken.
You should still define this in Animal (pseudocode):
boolean eatable = false;
fun isEatable(): boolean {
return eatable;
}
So you can define Animal.eat(Animal) to check if the animal you are trying to eat could be eaten.

If you can't give them different types, then I don't think there's any way the compiler could spot this problem.  However, you could make it more obvious by simply giving the parameter names:
ToDo(toEat = chickens, toFeed = cats)
That way, it's very clear which list goes with which parameter.  (It also means that the order of the parameters doesn't matter; you can swap them, as long as each is given once.)
It would still compile if you mixed them up:
ToDo(toEat = cats, toFeed = chickens)
…but at least that would be obvious and doesn't need you to check the documentation to see the problem.
A caller could still choose to omit the parameter names, though.  To prevent that, you'd have to restructure the ToDo class.  For example, you could use the builder pattern, and give something like:
ToDo().toEat(chickens).toFeed(cats)
Or you could simply remove the parameters from the constructor, requiring the caller to set properties by name, e.g.:
ToDo().apply{ toEat = chickens; toFeed = cats }
Those are a little more long-winded to call, of course, but force the caller to give the property names explicitly — again, making the problem fairly obvious to human readers (if not to the compiler).

Related

Kotlin. How to get specific subclass of sealed class?

I'm using kotlin sealed class. And I need to retrieve specific subclass. My sealed class:
sealed class Course(
val type: Type
) {
data class ProgrammingCourse(val name: String, val detail: String) : Course(Type.PROGRAMMING)
object LanguageCourse: Course(Type.LANGUAGE)
.....
}
For example I have function which can return Course:
fun getCourse(): Course {
if(...)
return Course.ProgrammingCourse("test", "test")
else
return Course.LanguageCourse
}
In addition, I have a method that can only work with a specific subclass of the Course class. Fox example:
fun workWithCourse(course: Course.ProgrammingCourse) {
// here some logic
}
And now I'm trying to get the course using the method getCourse(), and then pass it to the method workWithCourse()
fun main() {
val course = getCourse()
workWithCourse(course)
}
Error:
Type mismatch.
Required:
Course.ProgrammingCourse
Found:
Course
But I know the course type - Type, parameter that each course has. Can I, knowing this Type, cast the course (which I retrieve from getCourse() method) to a specific subclass ? Is there such a way ?
Please help me
P.S.
I don't need type checks like:
if(course is Course.ProgrammingCourse) {
workWithCourse(course)
}
I need the subclass to be automatically inferred by the Type parameter, if possible.
P.S.2
The need for such a solution is that I have a class that takes a Course, it doesn't know anything about a particular course, at the same time the class takes the Type that I want to use for identification. This class also receives an interface (by DI) for working with courses, a specific implementation of the interface is provided by the dagger(multibinding) by key, where I have the Type as the key. In the same way I want to pass by the same parameter Type specific subclass of my Course to my interface which working with specific courses.
No, there is no way for automatic inference to the best of my knowledge.
You returned a Course, and that's what you have. Being sealed here does not matter at all. Generally what you do here is use the when expression if you want to statically do different things depending on the type, but if it's just one type (ProgrammingCourse) that can be passed to workWithCourse, then an if is probably right, with dispatch using as.
That said, this looks like counter-productive design. If you can only work with one course, why do they even share a top level interface? The way the code is written implies working is a function that can take any course, or should be a method member. Anything else is very confusing. Perhaps workWithCourse should take a Course and use the when expression to dispatch it appropriately?
In kotlin you can specify the class explicitly with as.
val course = getCourse()
if (type == Type.PROGRAMMING) {
workWithCourse(course as Course.ProgrammingCourse)
}
*thanks Joffrey for his comment
What you seem to be asking for is a compile-time guarantee for something that will only be known at runtime. You didn't share the condition used in getCourse(), but in general it could return both types.
Therefore, you need to decide what will happen in both cases - that's not something the compiler can decide for you via any "inference".
If you want the program to throw an exception when getCourse() returns something else than a Course.ProgrammingCourse, you can cast the returned value using as:
val course = getCourse() as Course.ProgrammingCourse
workWithCourse(course)
If you don't want to crash, but you only want to call workWithCourse in some cases, then you need an if or when statement to express that choice. For instance, to call it only when the value is of type Course.ProgrammingCourse, then you would write the code you already know:
if (course is Course.ProgrammingCourse) {
workWithCourse(course)
}
Or with a when statement:
val course = getCourse()
when (course) {
is Course.ProgrammingCourse -> workWithCourse(course)
is Course.LanguageCourse -> TODO("do something with the other value")
}
The when is better IMO because it forces you (or other devs in the team) to take a look at this when whenever you (or they) add a new subclass of the sealed class. It's easy to forget with an if.
You can also decide to not test the actual type, and focus on the type property like in #grigory-panov's answer, but that is brittle because it relies on an implicit relationship between the type property and the actual type of the value:
val course = getCourse()
if (type == Type.PROGRAMMING) {
workWithCourse(course as Course.ProgrammingCourse)
}
The main point of using sealed classes is so you can use their actual type instead of a manually managed type property + casts. So I'd say use only is X and don't set a type property at all. Using a sealed class allows Kotlin to type-check a bunch of things, it's more powerful than using such a property.

Why is a Kotlin enum property not a compile time constant?

The primary target of this question is understanding the implementation and why it is like this. A solution or workaround for it would of course also be highly appreciated...
Given this example:
enum class SomeEnum(val customProp: String) {
FOO("fooProp"),
BAR("barProp");
}
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.SOURCE)
annotation class TheAnnotation(
val targetValue: String
)
#TheAnnotation(targetValue = SomeEnum.FOO.customProp)
fun testFun() {
}
The compilation results in the following error:
SomeEnum.kt: (14, 30): An annotation argument must be a compile-time constant
For obvious reasons, annotation values (along with others) must be compile-time constants, which makes sense in many different ways. What is unclear to me, is why customProp is not treated as a constant by the compiler.
If enums are defined as finite, closed sets of information, they should, in my understanding, only be mutable at compile-time a.k.a. "compile-time constant". For the unlikely case that enums somehow are modifiable at runtime in Kotlin, that would answer the question as well.
Addendum:
The enum value (e.g. SomeEnum.FOO) is actually treated as a compile-time constant. The proof is, that the following slightly changed snippet compiles:
enum class SomeEnum(val customProp: String) {
FOO("fooProp"),
BAR("barProp");
}
#Target(AnnotationTarget.FUNCTION)
#Retention(AnnotationRetention.SOURCE)
#MustBeDocumented
annotation class TheAnnotation(
val targetValue: SomeEnum
)
#TheAnnotation(targetValue = SomeEnum.FOO)
fun testFun() {
}
enums are defined as finite, closed sets of information, they should, in my understanding, only be mutable at compile-time
Actually, no. An enum class is just a special kind of class, that doesn't allow you to create any new instances other than the ones that you name in the declaration, plus a bunch more syntactic sugars. Therefore, like a regular class, it can have properties whose values are only known at runtime, and properties that are mutable (though this is usually a very bad idea).
For example:
enum class Foo {
A, B;
val foo = System.currentTimeMillis() // you can't know this at compile time!
}
This basically de-sugars into:
class Foo private constructor(){
val foo = System.currentTimeMillis()
companion object {
val A = Foo()
val B = Foo()
}
}
(The actual generated code has a bit more things than this, but this is enough to illustrate my point)
A and B are just two (and the only two) instances of Foo. It should be obvious that Foo.A is not a compile time constant*, let alone Foo.A.foo. You could add an init block in Foo to run arbitrary code. You could even make foo a var, allowing you to do hideous things such as:
Foo.A.foo = 1
// now every other code that uses Foo.A.foo will see "1" as its value
You might also wonder why they didn't implement a more restricted enum that doesn't allow you to do these things, and is a compile time constant, but that is a different question.
See also: The language spec
* Though you can still pass Foo.A to an annotation. To an annotation, Foo.A is a compile time constant, because all the annotation has to do, is to store the name "Foo.A", not the object that it refers to, which has to be computed at runtime.

Create an object of random class in kotlin

I learned java and python in high school and I became very comfortable with python. I have recently started to learn kotlin, mainly for fun (the keyword for defining a function is fun so it has to be a fun language, right), but I have a little problem.
Let's suppose I have a hierarchy of classes for Chess pieces:
abstract class Piece {
...
}
class Rook : Piece() {
...
}
class Bishop : Piece() {
...
}
.
.
.
I am taking input from the user to generate the board, so if the user types r, I need to create a Rook object, if he types b, I need to create a Bishop etc.
In python, I'd probably use a dictionary that maps the input string to the corresponding class, so I can create an object of the correct type:
class Piece:
...
class Rook(Piece):
...
class Bishop(Piece):
...
.
.
.
input_map = {
'r': Rook,
'b': Bishop,
...
}
s = input_map[input()]() # use user input as key and create a piece of the correct type
I was really amazed by this pattern when I discovered it. In java, I had to use a switch case or a bunch of if else if to achieve the same result, which is not the end of the world, especially if I abstract it into a separate function, but it's not as nice as the python approach.
I want to do the same thing in kotlin, and I was wondering if there is a similar pattern for kotlin since it's a modern language like python (I know, I know, python isn't new, but I think it's very modern). I tried to look online, but it seems like I can't store a class (class, not an object) in a variable or a map like I can in python.
Am I wrong about it? Can I use a similar pattern in kotlin or do I have to fall back to the when statement (or expression)?
If I am not mistaken, a similar pattern could be achieved in java using reflection. I never got to learn reflection in java deeply, but I know it's a way to use classes dynamically, what I can do for free in python. I also heard that in java, reflection should be used as a last resort because it's inefficient and it's considered "black magic" if you understand my meaning. Does it mean that I need to use reflection to achieve that result in kotlin? And if so, is it recommended to use reflection in kotlin, and is it efficient?
I'd like to know how I can approach this problem, and I accept multiple answers and additional solutions I didn't come up with. Thanks in advance.
This can be done without reflection.
You can map the input characters to the constructors:
val pieceConstructorsByKeyChar = mapOf(
'r' to ::Rook,
'b' to ::Bishop,
// etc.
)
Getting values from a map gives you a nullable, since it's possible the key you supply isn't in the map. Maybe this is fine, if when you use this you might be passing a character the player typed that might not be supported. Then you would probably handle null by telling the player to try again:
val piece: Piece? = pieceConstructorsByKeyChar[keyPressed]?.invoke()
Or if you do the look-up after you've already checked that it's a valid key-stroke, you can use !! safely:
val piece: Piece = pieceConstructorsByKeyChar[keyPressed]!!()
Yes you can use similiar approach with Kotlin. Kotlin has many features and supports reflection. Let me write an example about your problem.
Firstly create your classes that will be generate by user input.
abstract class Piece
class Rook : Piece()
class Bishop : Piece()
Create your class map
val inputMap = mapOf(
"r" to Rook::class.java,
"b" to Bishop::class.java
)
Create an instance what you want using newInstance function. If your input map doesn't contains key you gave then it will return null.
val rook = inputMap["r"]?.newInstance()
val bishop = inputMap["b"]?.newInstance()
// null
val king = inputMap["k"]?.newInstance()
Also you can write your custom extensions to create new objects.
fun <T> Map<String, Class<out T>>.newInstance(key: String) = this[key]?.newInstance()
// Create an instance with extension function
inputMap.newInstance("r")

Instantiate Kotlin class from string

I have a list of classes:
val availableClasses = listOf<Whatever>(
classA(),
classB(),
classC()
)
I am randomly selecting an item from this list using:
private var selection: Whatever = availableClasses.random()
Unfortunately, I think this approach is instantiating every class included in the list when the list is loaded.
I am hoping to work around this by replacing the list of classes with a list of strings:
val availableClasses = listOf<String>(
"classA",
"classB",
"classC"
)
Then once I have a selected string, instantiate only that one; something like:
private var selection: String = availableClasses.random()
// pseudo-code
val chosenClass = selection.toClass()
I can reference classes in Python using strings with the getattr function.
Is anything like this possible in Kotlin?
I'm also open to better approaches to this problem.
Instantiating classes by String name is more error-prone than using a constructor, because it relies on using a fully qualified, correctly spelled name, and the class having a specific constructor (either empty, or with specific arguments). So it can be done, but should be avoided when there are safer ways of doing it (ways where the compiler will give you an error if you're doing it wrong, instead of having an error occur only after you run the compiled program).
If I understand correctly, you want a list of classes that will only be instantiated one-at-a-time at random. One way to do this would be to make a list of class constructors.
val classConstructors = listOf<() -> Any>(
::ClassA,
::ClassB,
::ClassC
)
val randomInstantiatedClass = classConstructors.random()()

How does overload work in Kotlin without default params

I have a function that may take one or two parameters. In Java I would simply overload:
public myMethod( Cat cat, Dog dog){…}
public myMethod( Cat cat){…}
I understood that Kotlin has default params that would make overloading unnecessary. But these are objects for which I really know no default. So how do I proceed? And I don’t want to claim it’s nullable just for the sake of making null the default value. Any options I’m not seeing?
basically I don't want this
fun myMethod(cat:Cat, dog:Dog?=null) //it's never really nullable so don't want to pretend
I understood that Kotlin has default params that would make overloading unnecessary.
They don't; they make a specific (and very common in Java) usage of overloading unnecessary. If in Java you'd write
public myMethod(Cat cat){
myMethod(cat, new Dog(...)) // or myMethod(cat, null)
}
then in Kotlin you'd use a default argument. If you don't, then you use overloaded methods just like in Java, as mightyWOZ's answer shows.
Method (or function) overloading in kotlin works the same way as it works in java. That is you can specify multiple functions with same name but with different signature.
From Kotlin language specification
Kotlin supports function overloading, that is, the ability for several
functions of the same name to coexist in the same scope, with the
compiler picking the most suitable one when such a function is called.
So in your case if you don't want to use default parameters, then you can specify two different functions with same name but with different arguments.
So your java code can be converted to kotlin as.
fun myMethod(cat: Cat, dog: Dog){…}
fun myMethod(cat: Cat){…}
And you can call the overloaded functions as
var dog = Dog()
var cat = Cat()
myMethod(dog,cat)
myMethod(cat)
You can think as Dog is nullable in your in your Java method public myMethod( Cat cat){…}. There is no dog so it can be treated as null, since it doesn't exist. Then just check if it is null in kotlin and proceed as if it never was there.
First let's see what the Kotlin Language Documentation says:
Prefer declaring functions with default parameter values to declaring
overloaded functions.
And I don’t want to claim it’s nullable just for the sake of making
null the default value.
Then don't. So, you have to have some way of initializing dog.
You can specifiy a default value for dog right in the parameter list.
fun myMethod(cat: Cat, dog: Dog = Dog(...)) {
// ...
}
If you don't have a way of initializing dog when calling myMethod, it is not such a bad idea (as you might think) to make the parameter nullable. null means the value is absent and this is exactly the case.
fun myMethod(cat: Cat, dog: Dog? = null) {
// handle nullable dog
}