Cru
08/21/2023, 10:41 AM@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
sealed class ComparableAuthority<A : ComparableAuthority<A>> : Comparable<A> {
@Id
@GeneratedValue
open val id: Long? = null
@get:Transient
abstract val authority: String
}
@Entity
class SimpleNamedAuthority(
@Column(unique = true)
val authorityName: String
) : ComparableAuthority<SimpleNamedAuthority>() {
override fun compareTo(other: SimpleNamedAuthority): Int = authorityName.compareTo(other.authorityName)
override val authority: String = authorityName
override fun toString(): String = "SimpleNamedAuthority(authorityName='$authorityName')"
}
And other matching class hierarchy
sealed interface ComparableGrantedAuthority<A : ComparableGrantedAuthority<A>> : GrantedAuthority, Comparable<A>
data class SimpleNamedAuthority(val authorityName: String) : ComparableGrantedAuthority<SimpleNamedAuthority> {
override fun getAuthority(): String = authorityName
override fun compareTo(other: SimpleNamedAuthority): Int = authorityName.compareTo(other.authorityName)
}
Trying to create a converter between them
object AuthorityConverters {
fun <IN : ComparableAuthority<IN>, OUT : ComparableGrantedAuthority<OUT>> IN.toModel(): OUT =
when {
this is SimpleNamedAuthorityEntity -> toModel()
else -> throw IllegalStateException("BOOM")
}
fun SimpleNamedAuthorityEntity.toModel(): SimpleNamedAuthorityModel = SimpleNamedAuthorityModel(authorityName)
}
Results in compile-time-error Type mismatch: inferred type is SimpleNamedAuthority but OUT was expected
How can I best solve this issue?Szymon Jeziorski
08/22/2023, 11:39 AMOUT : ComparableGrantedAuthority<OUT>>
where OUT can be any ComparableGrantedAuthority
, but the function's body returns only SimpleNamedAuthorityModel
.
SimpleNamedAuthorityModel
may be the only subtype of sealed ComparableGrantedAuthority
, but that's not how the sealed class works. From generics standpoint OUT can be any ComparableGrantedAuthority
, so imagining there is type X
implementing ComparableGrantedAuthority
, it would be possible to invoke function with SimpleNamedAuthorityEntity
as IN
and X
as OUT
. SimpleNamedAuthorityModel
is not a subtype of X
therefore you have expected and actual type-mismatch.
Are you planning to add more branches to When? If not, why not just return SimpleNamedAuthorityModel
without using OUT
parameter at all?
If you are planning to add more branches and do not have any other compile time relation between IN
and OUT
, then I think you might have to omit OUT
from function generics and make the return type ComparableGrantedAuthority<*>
- This will allow you to return any ComparableGrantedAuthority
. You could then use exhaustive when
on the returned type to check which instance of the sealed class it is.
And if you are planning to map objects to specific types, then maybe it would be simpler to have different functions for different return types?Cru
08/23/2023, 1:22 PMGrantedAuthority
and authorization in general.
Currently, in all the examples that I have seen, authorization has been represented as a verb (or a role encompassing a number of verbs) that represents a specific action.
All of those implementations where known at compile-time (hence the sealed interface definition) and there were to sides of implementation one for the persistence layer and one to be used by the application (spring security, aka model). I was planning for the converter function that when I invoke it, I would specify which type I am returning and It would retain the type at compile-time. I was planning on a different GratnedAuthority
implementation where this is more data than just a String representing an action/verb.
Regardless, I'll redo that project again at some point in the future.