Reputation: 565
I'm gonna write a method like:
object UIBehavior {
fun dialog(context: Context, title: Int | String, message: Int | String){
val dialogObj = AlertDialog.Builder(context)
dialogObj.setTitle(title)
dialogObj.setMessage(message)
}
}
The methods dialogObj.setTitle and dialogObj.setMessage allow two types of parameters, and how can I delare the parameter that can let the method dialog allow only two types Int and String?
Upvotes: 14
Views: 27127
Reputation: 453
We can declare a variable with multiple types in Kotlin by combining :
sealed class ABClass<out T>
{
data class SealedClassA<out ClassA>(val value: ClassA) : ABClass<ClassA>()
data class SealedClassB<out ClassB>(val value: ClassB) : ABClass<ClassB>()
}
fun main() {
val a : ClassA = ClassA()
var b : ClassB = ClassB()
var s : String = "Just a string"
var c : ABClass<Any> = ABClass.SealedClassA(a)
c = ABClass.SealedClassB(b) // Works
c = ABClass() // Error : Sealed types cannot be instantiated
c = ABClass.SealedClassA(s) // Error : class java.lang.String cannot be cast to class Token
c = ABClass.SealedClassB(s) // Error : class java.lang.String cannot be cast to class Break
when(c)
{
is ABClass.SealedClassA -> { println("ABClassClassA ${(c.value as ClassA).AVariable}") }
is ABClass.SealedClassB -> { println("ABClassClassB ${(c.value as ClassB).BVariable}") }
}
}
open class ClassA
{
var AVariable : Int = 1
}
open class ClassB
{
var BVariable : Int = 10
}
Sealed class ABClass
has generic type T
.
The subsequent direct subclasses (SealedClassA & SealedClassB) of the sealed class are restricted to only be :
ClassA
for data class SealedClassA
or
ClassB
for data class SealedClassB
Thus constraining to type : ClassA or ClassB.
sealed class ABClass<out T>
{
data class SealedClassA<out ClassA>(val value: ClassA) : ABClass<ClassA>()
data class SealedClassB<out ClassB>(val value: ClassB) : ABClass<ClassB>()
}
var c : ABClass<Any>
Any
allows to store any type variable. But due to the definition of the sealed class only ClassA
or ClassB
types is allowed, others will give error thus putting constraint the type.
c = ABClass.SealedClassA(a)
c = ABClass.SealedClassB(b)
c = ABClass() // Error : Sealed types cannot be instantiated
c = ABClass.SealedClassA(s) // Error : class java.lang.String cannot be cast to class Token
c = ABClass.SealedClassB(s) // Error : class java.lang.String cannot be cast to class Break
It's possible to verify all cases, and else clause is not required.
when(c)
{
is ABClass.SealedClassA -> { println("ABClassClassA ${(c.value as ClassA).AVariable}") }
is ABClass.SealedClassB -> { println("ABClassClassB ${(c.value as ClassB).BVariable}") }
}
Variable c
can store essentially only two types ClassA
and ClassB
.
Upvotes: 1
Reputation: 71
fun <T>dialog(context: Context, title: T, message: T){
if(title !is String && title !is Int) throw InvalidParameterException()
val dialogObj = AlertDialog.Builder(context)
dialogObj.setTitle(title)
dialogObj.setMessage(message)
}
fun dialog(context: Context, title: Any, message: Any){
if(title !is String && title !is Int) throw InvalidParameterException()
val dialogObj = AlertDialog.Builder(context)
dialogObj.setTitle(title)
dialogObj.setMessage(message)
}
Upvotes: 3
Reputation: 565
Thank you guys, I was writing codes
interface DialogOption {
val title: Any
val message: Any
val positiveBtnTxt: Any
val negativeBtnTxt: Any
fun confirm(d: DialogInterface, n: Int) {
d.dismiss()
}
fun cancel(d: DialogInterface, n: Int) {
d.dismiss()
}
}
object UIBehavior {
fun dialog(context: Context, opt: DialogOption) {
val dialogObj = AlertDialog.Builder(context)
val title = opt.title
val message = opt.message
val poTxt = opt.positiveBtnTxt
val negTxt = opt.negativeBtnTxt
fun positiveCallback(d: DialogInterface, n: Int) {
opt.confirm(d, n)
}
fun negativeCallback(d: DialogInterface, n: Int) {
opt.cancel(d, n)
}
if (title is String) {
dialogObj.setTitle(title)
} else if (title is Int) {
dialogObj.setTitle(title)
}
if (message is String) {
dialogObj.setMessage(message)
} else if (message is Int) {
dialogObj.setMessage(message)
}
if (poTxt is String) {
dialogObj.setPositiveButton(poTxt, ::positiveCallback)
} else if (poTxt is Int) {
dialogObj.setPositiveButton(poTxt, ::positiveCallback)
}
if ( negTxt is String) {
dialogObj.setNegativeButton(negTxt, ::negativeCallback)
} else if (negTxt is Int) {
dialogObj.setNegativeButton(negTxt, ::negativeCallback)
}
dialogObj.show()
}
}
It works but not sure if it's reasonable
Upvotes: 0
Reputation: 80
You could use Any, but I don't think it is a very nice solution at all.
It would be much better to make both the title and message a String or CharSequence in line with the types setTitle and setMessage take as parameters.
Upvotes: 0
Reputation: 63955
You can't do that in Kotlin.
But you can have multiple versions of a function, e.g.
object UIBehavior {
fun dialog(context: Context, titleId: Int, messageId: Int){
val titleString = context.getString(titleId)
val messageString = context.getString(messageId)
dialog(context, titleString, messageString)
}
fun dialog(context: Context, title: String, message: String) {
val dialogObj = AlertDialog.Builder(context)
dialogObj.setTitle(title)
dialogObj.setMessage(message)
}
}
That way you can simply call the function with either ids or strings and it looks like you are using the same function
UIBehavior.dialog(this, R.string.title, R.string.message)
UIBehavior.dialog(this, "title", "message")
You could also use a common supertype of Int
and String
but that would allow a lot more and I wouldn't recommend that.
fun dialog(context: Context, title: Any, messageId: Any){
val titleString = when (title) {
is String -> title
is Int -> context.getString(title)
else -> throw IllegalArgumentException("Unsupported type")
}
val messageString = when ...
...
dialog(context, titleString, messageString)
}
Generics don't work here either because you can't call dialogObj.setTitle(title)
dynamically. It must be known at compile time whether you want to call the Int
or the String
overload of that function. It's also not really different from using Any
.
Upvotes: 16