About sealed subclass of a sealed class in Kotlin - kotlin

the code below compiles while it is impossible to go through conditions Color.Dark and Color.Light as these two classes are abstract.
Did I miss something ?
sealed class Color () {
sealed class Dark () {
class DarkRed : Color()
class DarkBlue : Color()
}
sealed class Light {
class LightRed : Color()
class LightBlue : Color()
}
}
fun eval(c: Color) =
when (c) {
is Color.Dark -> println("Dark")
is Color.Dark.DarkRed -> println("Dark Red")
is Color.Dark.DarkBlue -> println("Dark Blue")
is Color.Light -> println("Light")
is Color.Light.LightRed -> println("Light Red")
is Color.Light.LightBlue -> println("Light Blue")
}
fun main(args: Array<String>) {
val dr = Color.Dark.DarkRed()
eval(dr)
}

Two left indented conditions are never satisfied because Color.Dark and Color.Lightdo not inherit Color, and not because they are abstract. It means that 2 out of 6 is branches are useless, you can safely delete them. But other 4 branches (is Color.Dark.DarkRed, etc.) go through all Color subclasses, and Color is a sealed class, so when can be used without else branch.

Related

Generics in Objects

I have a question about sealed class, generics and object.
Let's say I would like to model something like 3 finite cases with a sealed class something like this:
sealed class ChangeState<S> {
fun reduceState(state: S): S
}
data class SetState<S>(val newState: S) : ChangeState<S>() {
override fun reduce(state: S): S = newState
}
object NoStateChange : ChangeState<Nothing>() { // What do I specify here for ChangeState? Nothing?
override fun reduce(state: Nothing): Nothing {
throw Exception("This should never be called")
}
}
The goal is to provide a convenient way to define NoStateChange in a generic way that it can be used as following:
fun foo(i : Int) : ChangeState<Int> {
return if (i==0)
NoStateChange // Won't compile because return type is ChangeState<Nothing> but expected ChangeState<Int>
else
SetState(i)
}
Is there a way to do that with object and Generics somehow?
As pointed out by #Tenfour04 the issue is that out is needed but reduceState() would require in as well. However, reduceState() can be refactored out of the class hierarchy and moved to an extension function like that:
sealed class ChangeState<out S>
data class SetState<S>(val newState: S) : ChangeState<S>()
object NoStateChange : ChangeState<Nothing>()
fun <S> ChangeState<S>.reduce(state: S): S {
return when (val change = this) {
is SetState -> change.newState
is NoStateChange -> state
}
}

Kotlin generic type mismatch

Is it possible to accept subclass of a class in generics? It works in Java but unfortunatelly I don't know how to make it work in Kotlin.
Simplified problem:
open class Food
class Meat : Food()
interface Animal<T : Food> {
fun eat(food : T)
}
class Dog : Animal<Meat> {
override fun eat(food: Meat) {
// Dog eats meat
}
}
lateinit var pet : Animal<Food>
fun <T : Food> setAnimal(animal: Animal<T>) {
pet = animal // DO NOT COMPILE
}
I would like to method setAnimal() accept all of the animals, no matter which food it likes, and I would like to keep reference to that animal. I thought Kotlin is 100% compatible with Java but when I try to convert class to Kotlin it doesn't convert well and I have to fix that issue. I read a lot of articles but still have no idea how to make it work.
EDIT:
Java code that solves problem but how to do that in Kotlin?
public class Test {
class Food {}
class Meat extends Food {}
interface Animal<T extends Food> {
void eat(T food);
}
class Dog implements Animal<Meat> {
#Override
public void eat(Meat food) {
}
}
private Animal animal;
public void setAnimal(Animal animal) {
this.animal = animal;
animal.eat(new Meat());
}
}
EDIT 2
Sample code which uses out Food, as it was proposed. I've marked line that do not compile.
open class Food
class Meat : Food()
interface Animal<T : Food> {
fun eat(food : T)
}
class Dog : Animal<Meat> {
override fun eat(food: Meat) {
// Dog eats meat
}
}
lateinit var pet : Animal<out Food>
fun <T : Food> setAnimal(animal: Animal<T>) {
val pet : Animal<out Food> = animal
pet.eat(Meat()) // DO NOT COMPILE
}
Use out modifier on pet variable definition
lateinit var pet : Animal<out Food>
More information https://kotlinlang.org/docs/reference/generics.html

How can I enforce that a class has a companion object?

What I want is this:
interface base {
abstract static fun foo()
}
class impl : base {
override static fun foo()
}
Normally, Kotlin solves problems using companion objects rather than static functions. But an interface can't define a requirement for a companion object with function. So how can I accomplish this? The code that uses this would look like
fun <T : base> bar() {
T.foo()
}
Any other way to get this behavior? Namely, that I can execute a function of a derivative of T, without knowing the specific type, and not assuming the derivative has a default constructor?
Edit
I was able to get this to do what I want by using value parameters of types that can be set on the companion objects of the classes I want to work with. An illustrative example of what I want to use this technique for.
import kotlin.reflect.full.*
interface DynamicBuilder {
fun build(sides: Int): Shape?
}
interface Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = null
}
}
abstract class Shape2D : Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = if(sides > 0) Square() else Circle()
}
}
abstract class Shape3D : Shape {
companion object : DynamicBuilder {
override fun build(sides: Int) = if(sides > 0) Cube() else Sphere()
}
}
class Square : Shape2D()
class Circle : Shape2D()
class Sphere : Shape3D()
class Cube : Shape3D()
fun Build(sides: Int, builder: DynamicBuilder): Shape? {
return builder.build(sides)
}
inline fun <reified T : Shape> Build(sides: Int): Shape? {
return Build(sides, T::class.companionObjectInstance as DynamicBuilder)
}
fun main() {
println(Build(0, Shape2D))
println(Build(4, Shape2D))
println(Build<Shape3D>(0))
println(Build<Shape3D>(6))
}
The goal is that I can create a new entire class of Shape, and have all the logic related to how it builds the concrete object contained in that file, rather than having some monolithic shared switch statement.
An interface can define a requirement for some object with function, and you can suggest it to be the companion object even if you can't force it to be.
interface BaseCompanion {
fun foo(): Unit
}
interface Base {
companion object : BaseCompanion {
fun foo() { println("in Base") }
}
fun companion(): BaseCompanion = Base
}
interface Derived : Base {
companion object : BaseCompanion {
fun foo() { println("in Derived") }
}
override fun companion() = Derived
}
// value parameter, not type parameter
fun bar(companion: BaseCompanion) {
companion.foo()
}
bar(Base)
bar(Derived)
The companion() function isn't actually used in this case, it's for when you want to access the companion from a Base instance:
fun baz(x: Base) {
x.companion().foo()
}
Another (unsafe) option is to define companion() using reflection.
fun companion() = this::class.companionObjectInstance as BaseCompanion
Plus: no need to explicitly override it in Derived; minuses: 1. will crash at runtime if you forget to create the companion or to extend BaseCompanion; 2. slower than non-reflection definition.
TL;TR:
How can I enforce that a class has a companion object?
You cannot.
Kotlin has no static methods. Even if it had them, they wouldn't be overridable, as they are not in Java. The same holds for companion objects. Kotlin code is eventually compiled to Java byte code, so what is not possible in Java won't be possible in Kotlin either.
Edit:
It's interesting to see what the compiler has to say about it. Consider the following snippet:
open class Base {
companion object {
fun test() {}
}
}
inline fun <reified T : Base> staticCall() {
T.test() // <-- ERROR
}
The error message:
Type parameter 'T' cannot have or inherit a companion object, so it cannot be on the left hand side of dot
Based on your updated question, it seems like what you want is usually achieved using the factory pattern. Alternatively you could also use dependency injection. There are many options without the usage of reflection.
Why shouldn't you use reflection?
There are a few reasons here and here and you can find more if you google it. Generally reflection was created for a specific purpose, to discover the functionality of a class that was unknown at compile time. You do not use it for this purpose, since your implementation requires you to know the class, in order to pass it as a reified generic parameter. If you do require to discover classes that you don't know at compile time, you can use dependency injection.
The simpler solution for your version is a factory pattern:
interface Shape
class Square : Shape
class Circle : Shape
class Sphere : Shape
class Cube : Shape
object ShapeFactory {
fun build2DShape(sides: Int): Shape {
if(sides > 0) Square() else Circle()
}
fun build3DShape(sides: Int): Shape {
if(sides > 0) Cube() else Sphere()
}
}
fun main() {
println(ShapeFactory.build2DShape(0))
println(ShapeFactory.build3DShape(0))
}
In short, Build<Shape3D>(0) is replaced by ShapeFactory.build3DShape(0). The caller still has to know that there are 3DShapes and where they are. The only thing that changed is that you do not require Reflection.
This requires the person calling the function to know of the existence of 2D and 3D shapes. Same as in your implementation with reflection. This way you can have all the logic how to create the shapes in the same file as the shapes. You could even make the factory call some functions in the companion object of the shape if you wish to do so. Your factory knows of the existence of those subclasses. But since you can put the factory in the same file as the subclasses, that doesn't split the logic to somewhere else.
If you want to delegate the deciding whether it is a 2D or a 3D shape to a subclass you can do the following:
interface Shape
class Square : Shape
class Circle : Shape
class Sphere : Shape
class Cube : Shape
object ShapeFactory {
fun build2DShape(sides: Int): Shape {
return if(sides > 0) Square() else Circle()
}
fun build3DShape(sides: Int): Shape {
return if(sides > 0) Cube() else Sphere()
}
}
fun getBuilder(dimensions: Int): (sides: Int) -> Shape {
if (dimensions == 2)
return ShapeFactory::build2DShape
else
return ShapeFactory::build3DShape
}
fun main() {
print (getBuilder(2)(3))
}

Reference outside the sealed class in Kotlin?

I'm trying to create a class that uses its own state to operate on the state of an external object that it holds a reference to. The external object can be of class A or B, which are similar, but not controlled by the author. So a sealed class is created to access their common attributes, per this earlier answer from #SimY4.
// *** DOES NOT COMPILE ***
class A { // foreign class whose structure is not modifiable
val prop get()= "some string made the Class-A way"
}
class B { // foreign class whose structure is not modifiable
val prop get()= "some string made the Class-B way"
}
data class ABTool (val obj:AB, val i:Int, val j:Int) {
// class that manipulates i and j and uses them to do
// things with AB's "common" attributes through the sealed class AB
sealed class AB { // substitute for a common interface
abstract val prop: String
abstract val addmagic: String
data class BoxA(val o:A) : AB() {
override val prop get()= o.prop
override val addmagic get() = prop + this#???.magic // HOW TO REFERENCE?
}
data class BoxB(val o:B) : AB() {
override val prop get()= o.prop
override val addmagic get() = this#???.magic + prop // HOW TO REFERENCE?
}
}
val magic get()= "magic: ${i*j}"
}
The problem now is that I've figured out I can't operate on the external object in the way I want, because a sealed class can't refer to its outer class members. Is there a better way to make this work, even if using a different approach (other than sealed class), while:
not changing foreign classes A or B;
respecting that A and B (and many others in the real case) are similar, so I'm trying to write one tool that calculates and adds magic to A and B with the same code base; and
noting that although the ABTool tools are the same, the way they are applied to add magic is slightly different in A vs. B, just as the to access the conceptually common elements of A and B may be different.
Any thoughts on this or a similar workaround? Maybe a more functional approach that I haven't conceived yet?
If ABTool being a sealed class is something you can give up, then here's a solution:
Replace sealed with inner abstract at the ABTool declaration;
Mark BoxA and BoxB as inner as well;
data class ABTool(val obj: AB, val i: Int, val j: Int) {
inner abstract class AB {
abstract val prop: String
abstract val addmagic: String
inner class BoxA(val o: A) : AB() {
override val prop get() = o.prop
override val addmagic get() = prop + magic
}
inner class BoxB(val o: B) : AB() {
override val prop get() = o.prop
override val addmagic get() = magic + prop
}
}
val magic get() = "magic: ${i * j}"
}
(alternatively, instead of marking AB as inner, move BoxA and BoxB out of it to the scope of ABTool)
An alternative would be to add an ABTool field to AB:
sealed class AB(val tool: ABTool) {
abstract val prop: String
abstract val addmagic: String
data class BoxA(val o:A, tool: ABTool) : AB(tool) {
override val prop get()= o.prop
override val addmagic get() = prop + tool.magic
}
data class BoxB(val o:B, tool: ABTool) : AB(tool) {
override val prop get()= o.prop
override val addmagic get() = tool.magic + prop
}
}
and pass this when creating it from ABTool. That's just what inner really does, after all.
In this specific case the field happens to be unused in AB itself and so you can remove it from there and make it val in BoxA and BoxB.

How to obtain all subclasses of a given sealed class?

Recently we upgraded one of our enum class to sealed class with objects as sub-classes so we can make another tier of abstraction to simplify code. However we can no longer get all possible subclasses through Enum.values() function, which is bad because we heavily rely on that functionality. Is there a way to retrieve such information with reflection or any other tool?
PS: Adding them to a array manually is unacceptable. There are currently 45 of them, and there are plans to add more.
This is how our sealed class looks like:
sealed class State
object StateA: State()
object StateB: State()
object StateC: State()
....// 42 more
If there is an values collection, it will be in this shape:
val VALUES = setOf(StateA, StateB, StateC, StateC, StateD, StateE,
StateF, StateG, StateH, StateI, StateJ, StateK, StateL, ......
Naturally no one wants to maintain such a monster.
In Kotlin 1.3+ you can use sealedSubclasses.
In prior versions, if you nest the subclasses in your base class then you can use nestedClasses:
Base::class.nestedClasses
If you nest other classes within your base class then you'll need to add filtering. e.g.:
Base::class.nestedClasses.filter { it.isFinal && it.isSubclassOf(Base::class) }
Note that this gives you the subclasses and not the instances of those subclasses (unlike Enum.values()).
With your particular example, if all of your nested classes in State are your object states then you can use the following to get all of the instances (like Enum.values()):
State::class.nestedClasses.map { it.objectInstance as State }
And if you want to get really fancy you can even extend Enum<E: Enum<E>> and create your own class hierarchy from it to your concrete objects using reflection. e.g.:
sealed class State(name: String, ordinal: Int) : Enum<State>(name, ordinal) {
companion object {
#JvmStatic private val map = State::class.nestedClasses
.filter { klass -> klass.isSubclassOf(State::class) }
.map { klass -> klass.objectInstance }
.filterIsInstance<State>()
.associateBy { value -> value.name }
#JvmStatic fun valueOf(value: String) = requireNotNull(map[value]) {
"No enum constant ${State::class.java.name}.$value"
}
#JvmStatic fun values() = map.values.toTypedArray()
}
abstract class VanillaState(name: String, ordinal: Int) : State(name, ordinal)
abstract class ChocolateState(name: String, ordinal: Int) : State(name, ordinal)
object StateA : VanillaState("StateA", 0)
object StateB : VanillaState("StateB", 1)
object StateC : ChocolateState("StateC", 2)
}
This makes it so that you can call the following just like with any other Enum:
State.valueOf("StateB")
State.values()
enumValueOf<State>("StateC")
enumValues<State>()
UPDATE
Extending Enum directly is no longer supported in Kotlin. See
Disallow to explicitly extend Enum class : KT-7773.
With Kotlin 1.3+ you can use reflection to list all sealed sub-classes without having to use nested classes: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.reflect/-k-class/sealed-subclasses.html
I asked for some feature to achieve the same without reflection: https://discuss.kotlinlang.org/t/list-of-sealed-class-objects/10087
Full example:
sealed class State{
companion object {
fun find(state: State) =
State::class.sealedSubclasses
.map { it.objectInstance as State}
.firstOrNull { it == state }
.let {
when (it) {
null -> UNKNOWN
else -> it
}
}
}
object StateA: State()
object StateB: State()
object StateC: State()
object UNKNOWN: State()
}
A wise choice is using ServiceLoader in kotlin. and then write some providers to get a common class, enum, object or data class instance. for example:
val provides = ServiceLoader.load(YourSealedClassProvider.class).iterator();
val subInstances = providers.flatMap{it.get()};
fun YourSealedClassProvider.get():List<SealedClass>{/*todo*/};
the hierarchy as below:
Provider SealedClass
^ ^
| |
-------------- --------------
| | | |
EnumProvider ObjectProvider ObjectClass EnumClass
| |-------------------^ ^
| <uses> |
|-------------------------------------------|
<uses>
Another option, is more complicated, but it can meet your needs since sealed classes in the same package. let me tell you how to archive in this way:
get the URL of your sealed class, e.g: ClassLoader.getResource("com/xxx/app/YourSealedClass.class")
scan all jar entry/directory files in parent of sealed class URL, e.g: jar://**/com/xxx/app or file://**/com/xxx/app, and then find out all the "com/xxx/app/*.class" files/entries.
load filtered classes by using ClassLoader.loadClass(eachClassName)
check the loaded class whether is a subclass of your sealed class
decide how to get the subclass instance, e.g: Enum.values(), object.INSTANCE.
return all of instances of the founded sealed classes
If you want use it at child class try this.
open class BaseSealedClass(val value: String, val name: Int) {
companion object {
inline fun<reified T:BaseSealedClass> valueOf(value: String): T? {
return T::class.nestedClasses
.filter { clazz -> clazz.isSubclassOf(T::class) }
.map { clazz -> clazz.objectInstance }
.filterIsInstance<T>()
.associateBy { it.value }[value]
}
inline fun<reified T:BaseSealedClass> values():List<T> =
T::class.nestedClasses
.filter { clazz -> clazz.isSubclassOf(T::class) }
.map { clazz -> clazz.objectInstance }
.filterIsInstance<T>()
}
}
#Stable
sealed class Theme(value: String, name: Int): BaseSealedClass(value, name) {
object Auto: Theme(value = "auto", name = R.string.setting_general_theme_auto)
object Light: Theme(value= "light", name = R.string.setting_general_theme_light)
object Dark: Theme(value= "dark", name = R.string.setting_general_theme_dark)
companion object {
fun valueOf(value: String): Theme? = BaseSealedClass.valueOf(value)
fun values():List<Theme> = BaseSealedClass.values()
}
}
For a solution without reflection this is a library that supports generating a list of types to sealed classes at compile time:
https://github.com/livefront/sealed-enum
The example in the docs
sealed class Alpha {
object Beta : Alpha()
object Gamma : Alpha()
#GenSealedEnum
companion object
}
will generate the following object:
object AlphaSealedEnum : SealedEnum<Alpha> {
override val values: List<Alpha> = listOf(
Alpha.Beta,
Alpha.Gamma
)
override fun ordinalOf(obj: Alpha): Int = when (obj) {
Alpha.Beta -> 0
Alpha.Gamma -> 1
}
override fun nameOf(obj: AlphaSealedEnum): String = when (obj) {
Alpha.Beta -> "Alpha_Beta"
Alpha.Gamma -> "Alpha_Gamma"
}
override fun valueOf(name: String): AlphaSealedEnum = when (name) {
"Alpha_Beta" -> Alpha.Beta
"Alpha_Gamma" -> Alpha.Gamma
else -> throw IllegalArgumentException("""No sealed enum constant $name""")
}
}
The short version is
State::class.sealedSubclasses.mapNotNull { it.objectInstance }