Norbi
11/24/2023, 11:12 AMinterface Formatter<in T> {
fun supports(value: Any?): Boolean
fun format(value: T): String
}
class IntFormatter : Formatter<Int> {
override fun supports(value: Any?): Boolean = value is Int
override fun format(value: Int): String = value.toString()
}
class DoubleFormatter : Formatter<Double> {
override fun supports(value: Any?): Boolean = value is Double
override fun format(value: Double): String = value.toString()
}
interface GenericFormatter {
fun format(value: Any?): String
}
class GenericFormatterImpl(
private val formatters: List<Formatter<*>>
): GenericFormatter {
override fun format(value: Any?): String =
formatters
.firstOrNull { it.supports(value) }
?.format(value) // Compilation error: Type mismatch. Required: Nothing Found: Any?
?: value.toString()
}
val genericFormatter: GenericFormatter = GenericFormatterImpl(listOf(IntFormatter(), DoubleFormatter()))
Thanks.ephemient
11/24/2023, 11:36 AMformatters
but it likely has type List<Formatter<Nothing>>
, so you can't do anything without some unchecked casting, e.g. as Formatter<Any?>?
before invoking ?.format(value)
aishwaryabhishek3
11/24/2023, 11:45 AMfun format(value: Any?): String =
formatters
.firstOrNull { it.supports(value) }?.let {
(it as? Formatter<Any?>)?.format(value)
} ?: value.toString()
Norbi
11/24/2023, 11:59 AMephemient
11/24/2023, 12:11 PMin *
is effectively Nothing
, ditto out *
and Any?
as per the docsephemient
11/24/2023, 12:11 PMNorbi
11/24/2023, 12:25 PMGenericFormatterImpl
to
class GenericFormatterImpl(
private val formatters: List<Formatter<Any?>>
): GenericFormatter {
then it compiles.
But in this case the instantiation does not compile:
val genericFormatter: GenericFormatter = GenericFormatterImpl(listOf(IntFormatter(), DoubleFormatter())) // Type mismatch. Required: List<Formatter<Any?>> Found: List<Formatter<*>>
Joffrey
11/24/2023, 1:10 PMFormatter<Int>
is NOT a Formatter<Any?>
because it cannot format other things than ints (e.g. it cannot format a Double
). A Formatter<Any?>
is expected to be able to take in any value of type Any?
(which is basically anything).
This constraint comes from the position of the T
as argument to the format()
method, and it is formalized by using in T
in the Formatter
declaration. Both of those make sense, and rightfully forbid you from putting a `Formatter<Int>`in a List<Formatter<Any?>>
Norbi
11/24/2023, 1:39 PMclass GenericFormatterImpl(
private val formatters: List<Formatter<*>>
) : GenericFormatter {
override fun format(value: Any?): String =
(formatters
.firstOrNull { it.supports(value) }
as? Formatter<Any?> // Explicit cast
)
?.format(value)
?: value.toString()
}
Joffrey
11/24/2023, 1:41 PMformatters
property itself. It's rather that you have information that the compiler doesn't know: the fact that formatter.supports(value) == true
means that the type of value
is a subtype of T
for this formatter
.
Since the compiler cannot deduce this on its own (at least in the current design), you have to castCLOVIS
11/24/2023, 1:44 PMas Formatter<WhateverYouWant>
only checks that the class inherits from Formatter
, it cannot check whether the generic type is correct or not. If you had an incorrect implementation of supports
, I think this crashes at runtime on the format
call, since the as
will always succeedJoffrey
11/24/2023, 1:47 PMsupports()
. Of course if it turns out we're wrong (someone broke this contract), then telling the compiler "I know better" will lead to incorrect behaviour or runtime crashes.Daniel Pitts
11/24/2023, 7:42 PMinterface Formatter {
fun tryFormat(value: Any?): String?
}
class IntFormatter : Formatter {
override fun tryFormat(value: Any?): String? = (value as? Int)?.let(Int::toString)
}
class DoubleFormatter : Formatter {
override fun tryFormat(value: Any?): String? = (value as? Int)?.let(Int::toString)
}
class GenericFormatter(private val formatters: List<Formatter>):Formatter {
override fun tryFormat(value: Any?): String? =
formatters.asSequence().map { it.tryFormat(value) }.firstOrNull()
}
Daniel Pitts
11/24/2023, 7:43 PMtryFormat
will provide a cleaner impl