0

I'm newbie of Kotlin. I'm learning sealed classes and I don't understand how could I use it in Android development. Can you give me an example?

Ori Marko
  • 56,308
  • 23
  • 131
  • 233
omega1100100
  • 39
  • 1
  • 6
  • 1
    I think [this](https://www.raywenderlich.com/75657-kotlin-sealed-classes) contains a nice example... apart from the fact that one is not well advised to use double for the representation of currency values. – deHaar Dec 19 '19 at 09:16

2 Answers2

3

It's useful when you want to define a closed list of subclasses.

You can use possibility for data classes to extend sealed classes . Example:

 fun eval(expr: Expr): Double = when(expr) {
   is Const -> expr.number
   is Sum -> eval(expr.e1) + eval(expr.e2)
   NotANumber -> Double.NaN
  // the `else` clause is not required because we've covered all the cases
 }

Declaration:

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
coroutineDispatcher
  • 7,718
  • 6
  • 30
  • 58
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
0

Kotlin documentation says:

Sealed classes are used for representing restricted class hierarchies, when a value can have one of the types from a limited set, but cannot have any other type.

Let's take a simple example. You have a sealed abstract class that represents a boolean expression:

sealed class BooleanExpression {
    abstract fun evalate(): Boolean
}

Now let's define a class that extends this sealed class:

class OrBooleanExpression(val elem1: Boolean, val elem2: Boolean) : BooleanExpression() {
    override fun evalate() = elem1 or elem2
}

Let's now assume for the example that we want a method that prints only the members of a boolean expression. For our or boolean expression, we would have the following:

Elem1 true / Elem2 false

We could implement our method like the following:

fun printMembers(expr: BooleanExpression) = when (expr) {
    is OrBooleanExpression -> print("Elem1 ${expr.elem1} / Elem2 ${expr.elem2}")
}

Up to that point, our compiler is happy. Nothing is wrong. In fact, in our when, we have taken into account all subclasses of the sealed class.

Let's add now another boolean expression:

class NotBooleanExpression(val elem1: Boolean) : BooleanExpression() {
    override fun evalate(): Boolean = !elem1
}

Now the compiler returns an error:

'when' expression must be exhaustive, add necessary 'is NotBooleanExpression' branch or 'else' branch instead

Now we have two possilities to solve this problem. The first is to add a clause for the new operation:

fun printMembers(expr: BooleanExpression) = when (expr) {
    is OrBooleanExpression -> print("Elem1 ${expr.elem1} / Elem2 ${expr.elem2}")
    is NotBooleanExpression -> print("Elem1 ${expr.elem1}")
}

Or we could add a else clause:

fun printMembers(expr: BooleanExpression) = when (expr) {
    is OrBooleanExpression -> print("Elem1 ${expr.elem1} / Elem2 ${expr.elem2}")
    else -> print("Unknown elements")
}

In both case, the compilation works because we have handled all subclasses of the sealed class.

If now we take into account a langage that doesn't have currently sealed class, like Java. We won't be able to do that at compile time. You will therefore need to implement the following using the design pattern Visitor.

interface BooleanExpression {
    abstract Boolean evaluate();

    abstract <T> T accept(Visitor<T> visitor);
}

class NotBooleanExpression implements BooleanExpression {
    private String elem1;    

    public NotBooleanExpression(String elem1) {
        this.elem1 = elem1;
    }

    public Boolean getElem1() {
        return elem1;
    }

    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

class OrBooleanExpression implements BooleanExpression {
    private String elem1;
    private String elem2;   

    public NotBooleanExpression(String elem1, String elem2) {
        this.elem1 = elem1;
        this.elem2 = elem2;
    }

    public Boolean getElem1() {
        return elem1;
    }

    public Boolean getElem2() {
        return elem2;
    }

    @Override
    public <T> T accept(Visitor<T> visitor) {
        return visitor.visit(this);
    }
}

class Visitor<T> {
    T visit(NotBooleanExpression expr);

    T visit(OrBooleanExpression expr);
}

class Test {
    public void printMembers(expr: BooleanExpression) {
        expr.accept(new Visitor<Void>() {
            @Override
            public Void visit(NotBooleanExpression expr) {
                System.out.println("Elem1 " + expr.getElem1());

                return null;
            }

            @Override
            public Void visit(OrBooleanExpression expr) {
                System.out.println("Elem1 " + expr.getElem1() + " / Elem2" + expr.getElem2());

                return null;
            }
        };
    }

}
D. Lawrence
  • 943
  • 1
  • 10
  • 23