Practical example of sealed classes in Android Development [duplicate] - kotlin

This question already has answers here:
What are sealed classes in Kotlin?
(3 answers)
Closed 3 years ago.
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?

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()

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;
}
};
}
}

Related

get Not enough information to infer type variable T when initializing kotlin raw types

I tried to write raw types for. kotlin in a class and initialized the raw type
class RawType {
interface I<T : RawType?> {
fun f(t: T) {}
}
class RawSubclass: I<T: RawType?> {
override fun f(t: RawType?) {}
}
fun test() {
val unbound = Unbound<T: RawType?>()
}
}
the java I can write
class RawType {
interface I<T extends RawType> {
default void f(T t) {}
}
class BoundRecursively<T extends BoundRecursively<T>> {}
static class RawSubclass implements I {
#Override
public void f(RawType t) {}
}
test() {
Unbound unbound = new Unbound();
}
}
but it gives error Expecting a '>' Expecting member declaration for line class RawSubclass: I<T: RawType?> and Not enough information to infer type variable T for val unbound = Unbound<T: RawType?>(). Any suggestion why the error occurs and how to fix it? Thanks

Calling an overloaded method with the base class parameter type

Is it possible in Kotlin to call an overloaded method using the base class type as a parameter? This is best illustrated via an example
Base Sealed Class + Derived Classes
sealed class Event {
abstract val eventId: String
}
data class FirstEvent(
override val eventId: String
val first: String
) : Event()
data class SecondEvent(
override val eventId: String
val second: String
) : Event()
Utility Class having an overloaded method for each of the derived classes
class UtilityClass {
fun handle(event: FirstEvent) {
....
}
fun handle(event: SecondEvent) {
....
}
}
Is it possible to call methods of the utility class in such a way utility.handle(FirstEvent("id", "first) as Event) doing so is giving me the following exception
None of the following functions can be called with the arguments supplied.
you can do something like this
fun handleEvent(event: Event) {
when (event) {
is FirstEvent -> {
// event is automatically casted as FirstEvent
event.first
}
is SecondEvent -> ...
}
}

Passing and Accessing Values and Functions of a Subtype that was passed into a Class Constructor requiring it's Supertype

I'll keep this as simple as possible. Let's say I have a parent class with a function as below that takes a position argument as a Point data class
open class Obj(val pos:Point) {
fun foo() : Double {
return 5.0 + pos.x * pos.y * pos.z
}
}
For sake of thoroughness, here is the Point data class
data class Point(val x:Double, val y:Double, val z:Double)
So I have multiple children that inherit from the Obj class but implement an additional function that is named the same in every child and calls a function in the parent,
class Cube(pos:Point) : Obj(pos) {
fun number() : Double {
return 10.0 * foo()
}
}
class Sphere(pos:Point) : Obj(pos) {
fun number() : Double {
return 20.0 * foo()
}
}
My question is, if I have a function somewhere that takes in objects that inherit from Obj but not Obj itself, how can I ensure they are of their own subtype rather than the Supertype?
For example, I currently have my function looking like this
fun foo2(obj:Obj) {
println(obj.number()) // throws error because obj doesn't have method number()
}
The normal approach to this sort of case would be to add an abstract method to the base class, and have the subclasses implement it. That way, whenever you have a base class reference, the compiler knows that method is available.
That would require the base class itself to be abstract, so you can't instantiate it directly. (And if foo() is only used by number() implementations, it might make sense to hide it from other classes.)
abstract class Obj(val pos: Point) {
abstract fun number(): Double
protected fun foo() = 5.0 + pos.x * pos.y * pos.z
}
class Cube(pos: Point) : Obj(pos) {
override fun number() = 10.0 * foo()
}
If, however, you need the base class to be instantiable, then that's more tricky: there's no easy way to specify 'only subclasses'. Depending on your exact requirements, you might allow the base class to be passed, and have it provide a default implementation of number():
open fun number() = foo()
Okay so using the suggestion from Animesh Sahu, I've implemented an Interface called ObjI in the base class, and required each implementation override the number() function. I combined that with the answer given by gidds, suggesting creating a function that calls another function. So the number() function in the base class just calls the foo() function
data class Point(val x:Double, val y:Double, val z:Double)
interface ObjI {
fun number() : Double
}
open class Obj(val p:Point) : ObjI {
override fun number() = foo()
fun foo() : Double {
return 5.0 + p.x * p.y * p.z
}
}
class Sphere(p:Point) : Obj(p) {
override fun number() : Double {
return 10.0 * super.foo()
}
}
class Cube(p:Point) : Obj(p) {
override fun number() : Double {
return 20.0 * super.foo()
}
}
fun main(args: Array<String>) {
val s = Sphere(Point(13.0, 6.0, 1.0))
val c = Cube(Point(13.0, 6.0, 1.0))
printem(s)
printem(c)
}
fun printem(o:Obj) {
println(o.number())
}

JUnit 5 and Arguments.of() with functions

Writing a JUnit 5 parameterized test and need to pass functions to the test using Arguments.of(), but there are 2 compile errors that I don't know how to fix. Any help would be appreciated.
The method of(Object...) in the type Arguments is not applicable for the arguments (boolean, String::length)
The target type of this expression must be a functional interface
public static Stream<Arguments> some() {
return Stream.of(Arguments.of(true, String::length));
}
#ParameterizedTest
#MethodSource
public <T> void some(final T input, final Function<String, Integer> length) {
}
The following works as expected.
public void sample() {
some(true, String::length);
}
Wrap the arguments in a helper method
Similar to the answer "wrap it in a class", but possibly less intrusive, is to use a helper method to pass the functional interface as a java.lang.Object.
For example, the first raw method reference, Math::ciel, in this parameterized test:
#ParameterizedTest
#MethodSource("testCases")
void shouldExerciseMethod(Function<Double, Double> method, Double expected) {
assertEquals(expected, method.apply(1.5d), 1.0E-8d);
}
static Stream<Arguments> testCases() {
return Stream.of(Arguments.of(Math::ceil, 2.0d),
Arguments.of(Math::floor, 1.0d));
}
causes this compilation error:
java: method of in interface org.junit.jupiter.params.provider.Arguments cannot be applied to given types;
required: java.lang.Object[]
found: Math::ceil,double
reason: varargs mismatch; java.lang.Object is not a functional interface
which you can get around by passing the arguments through a helper method:
static <T, U> Arguments args(Function<T, U> method, U expected) {
return Arguments.of(method, expected);
}
so:
static Stream<Arguments> testCases() {
return Stream.of(args(Math::ceil, 2.0d),
args(Math::floor, 1.0d));
}
My attempts to make the idiom more general using varargs failed with variations on the same error, so I have ended up overloading it whenever I need another signature.
The function needs to be wrapped in a class.
public static class P {
private final Function<String, Integer> mFunction;
public P(final Function<String, Integer> function) {
mFunction = function;
}
public Function<String, Integer> function() {
return mFunction;
}
}
public static Stream<Arguments> some() {
return Stream.of(Arguments.of(3, "abc", new P(String::length)));
}
#ParameterizedTest
#MethodSource
public <T> void some(final int expect, final String input, final P p) {
assertEquals(expect, p.function().apply(input));
}
I liked #adrian-redgers solution, but I think overloading a method for each signature needed is a bit overkill.
You only really need to convert the functional interface to an object. So the solution I implemented was:
/**
* Helps to use {#link org.junit.jupiter.params.provider.Arguments#of(Object...)}, as functional
* interfaces cannot be converted into an object directly.
*/
public class ArgumentsWrapper {
private ArgumentsWrapper() {
throw new IllegalStateException(
ArgumentsWrapper.class + " util class cannot be instantiated");
}
public static <T, U> Function<T, U> wrap(Function<T, U> function) {
return function;
}
}
Then, it can be used as:
public static Stream<Arguments> testMapAlarmTypeConfigWithLanguage() {
return Stream.of(
// Statically imported ArgumentsWrapper#wrap
Arguments.of(null, wrap(AlarmTypeConfig::getNameInEnglish)),
Arguments.of("en-us", wrap(AlarmTypeConfig::getNameInEnglish)),
Arguments.of("es-es", wrap(AlarmTypeConfig::getNameInSpanish)));
}

How to overcome "same JVM signature" error when implementing a Java interface?

With the code below, I am getting the following error in IntelliJ IDEA 13.1.6 and Kotlin plugin 0.11.91.AndroidStudio.3:
Platform declaration clash: The following declarations have the same JVM signature (getName()Ljava/lang/String;):
• public open fun getName(): kotlin.String?
• internal final fun <get-name>(): kotlin.String?
Java class, JavaInterface.java:
public interface JavaInterface {
public String getName();
}
Kotlin class, KotlinClass.kt
public class KotlinClass(val name: String?) : JavaInterface
I've tried overriding the 'getter' method by
adding override fun getName(): String? = name, but that produces the same error.
I can see one workaround by doing this instead:
public class KotlinClass(val namePrivate: String?) : JavaInterface {
override fun getName(): String? = namePrivate
}
But in my real-world case I have a number of properties to implement and need setters too. Doing this for each property doesn't seem very Kotlin-ish. What am I missing?
Making that variable private solves the problem.
public class KotlinClass(private val name: String?) : JavaInterface
You could use #JvmField for instructs the compiler not generate getter/setter, and you can implement your setters and getters. With this your code work well in Java (as attribute getter/setter) and Kotlin as property
Example:
JAVA:
public interface Identifiable<ID extends Serializable>
{
ID getId();
}
KOTLIN:
class IdentifiableImpl(#JvmField var id: String) :Identifiable<String>
{
override fun getId(): String
{
TODO("not implemented")
}
}
The annotation feature of Kotlin named #JvmName will solve the duplication problem in Java and Kotlin when having the same signature.
fun function(p: String) {
// ...
}
// Signature: function(Ljava/lang/String)
With the use of JvmName will be:
#JvmName("functionOfKotlin")
fun function(p: String) {
// ...
}
// Signature: functionOfKotlin(Ljava/lang/String)
IMHO most readable combination is field + explicit interface implementation by the single-expression function (combination of #Renato Garcia's and #Steven Spungin's answers):
Java:
public inteface SomeInterface {
String getFoo();
}
Kotlin:
class Implementation(#JvmField val foo: String) : SomeInterface {
override fun getFoo() = foo
}
Another work-around is to declare the properties in an abstract Kotlin class, then write a small java class that extends KotlinClass and implements JavaInterface.
// JavaInterface.java
public interface JavaInterface {
int getFoo();
void setFoo(int value);
}
// KotlinClass.kt
abstract class KotlinClass(open var foo : Int = 0) {
}
// JavaAdapter.java
class JavaAdapter extends KotlinClass implements JavaInterface {
// all code in KotlinClass, but can't implement JavaInterface there
// because kotlin properties cannot override java methods.
}
We have found that to use the same names without clashing, the ctor args must be private AND you must still override the interfaces methods. You don't need any additional backing fields. Also, your expression body assignment will not recurse, so you can safely use that syntax.
Java Interface
interface IUser {
String getUserScope();
String getUserId();
}
Kotlin Class
class SampleUser(private val userScope: String, private val userId: String) : IUser {
override fun getUserId() = userId
override fun getUserScope() = userScope
}
If you have direct control over the interface then the best approach is to write the interface in Kotlin. You can then write your class
public class KotlinClass(override val name: String?) : KotlinInterface
and still reference it from any Java code using the same interface as before. This looks a lot neater than setting all the properties to private and overriding the get function. Obviously if you can't migrate the interface to Java because you don't own it then that seems to be the only solution.
public interface JavaInterface {
public String getName();
}
public class KotlinClass(val namePrivate: String?) : JavaInterface {
private var name = namePrivate
override fun getName(): String? {
return name
}
}
Rename the variable to something else, or make it private if u dont want it to be public.
convert function to property instead of initializing property from a function.
for ex:
fun getCountriesList(): List<Country> {
val countries = mutableListOf<Country>()
countries.add(Country("in", "+91", "India", R.drawable.indian_flag))
countries.add(Country("us", "+1", "United States",R.drawable.us_flag))
return countries
}
to
val countriesList: List<Country>
get() {
val countries = mutableListOf<Country>()
countries.add(Country("in", "+91", "India", R.drawable.indian_flag))
countries.add(Country("us", "+1", "United States", R.drawable.us_flag))
return countries
}