Although you can’t do it exactly as you’d like - and it may be asking too much for a future type inference algorithm to sort that one out - you can do it more concisely using a when statement:
val someValue: Any = 0 // or 0L
when (someValue) {
is Int -> foo(someValue)
is Long -> foo(someValue)
}
Hey thanks for pointing out. Kotlin praises that I reduce code to the minimum.
I was opened this discuss, because In my case I have do
val scope = viewModelScopeListener!!.lifecycleScopeFragmentOrActivity
if(scope is FragmentActivity){
mViewModel = ViewModelProviders
.of(scope)
.get(viewModelClass)
}else if(scope is Fragment)
mViewModel = ViewModelProviders
.of(scope)
.get(viewModelClass)
I have only 2 cases, but what if I would have more them e.g. using some factory method. Are you able to considern this issue as TODO for future improvements?
Well you can, of course, always add extra ‘cases’ to the when statement if you have more than two types to check.
The Kotlin design team are always looking for opportunities to improve smart casting provided this doesn’t increase compile time significantly and there are some improvements in this regard coming in the forthcoming version 1.2.
However, the reason why I think you may be asking too much for smart casting to be extended to your situation is because the compiler would no longer be able to determine statically which function overload was to be called.
To deal with this either some form of late binding would need to be used (which would impact runtime performance) or the compiler would need to rewrite the code so there were branches for each type being checked - in other words you would end up with a ‘when’ statement.
Frankly, I just don’t see this as being a significant enough ‘pain’ point for the compiler to write code which you could easily - albeit repetitively - have written yourself.
What you are asking for is known as multiple dispatch, which is to choose which method is called based on the run-time type of the parameter. Kotlin, like Java and a multitude of other languages, does not support multiple dispatch. To read more about multiple dispatch and the way to emulate it in a language that does not support it see: Multiple dispatch - WikipediaDouble dispatch - Wikipedia.
I have faced the need for it before in Dependency Injection (which looks like what you are doing as well). The lack of multiple dispatch is what makes Dagger 2 very problematic on Android. It is very easy to use Dagger 2 on Android and lose the entire reason you are doing dependency injection, which is to allow control injections at run-time.
To simulate multiple dispatch you either have to have if-else/ when or else a Map. You can do things to hide it or automate generating it, but it will have to exist somewhere. What I did to work around Dagger 2 on Android was to create an Annotation Processor that creates wrappers for Dagger 2 that create a simulated multiple dispatch by building a map. You can see that project here: NobleworksSoftware / NobleInjection · GitLab
Edit: corrected incorrect terminology of multiple dispatch vs. double dispatch
Because it doesn’t solve the problem, which is to invoke the appropriate code based on the run-time type of a value. Generics are also about the compile-time type of an expression not the run-time type.
You can write it, but it isn’t going to compile, because once again Kotlin does not have multiple dispatch and it has to choose what method it is going to call at compile time based on the compile time type of the argument.
Here is a simplified version of the original problem and it does not compile:
import java.util.Random;
class Foo
class Bar
object Baz
{
fun qux(f: Foo) = "A Foo"
fun qux(b: Bar) = "A Bar"
}
fun main(args: Array<String>)
{
val v = if(Random().nextInt(2) == 0) Foo() else Bar()
when(v)
{
is Foo, is Bar -> println(Baz.qux(v))
}
}
Edit: corrected incorrect terminology of multiple dispatch vs. double dispatch
OK, becuase .of(…) is overloaded? I assumed that FragmentActivity and Fragment were subclasses. Silly me. Well, the generics would probably be the answer as the other guy says, if there is a lot of code but in the example it’s hardly the end of the world though. Every minor syntax inconvenience doesn’t justify a change in syntax.
Personally, I think overloading is broken, but overloading on something other than the static type of the parameters is overkill and wildly inefficient. Maybe the OP can choose a fully functional language and have algebraic types to his heart’s content.
I think being able to declare a subtype relation between types is the general answer and would solve these sorts of problems more elegantly than overloading.
No type relationship between them. Activities and Fragments are Android types and while they have some common characteristics they are not related in the type hierarchy. A FragmentActivity is an activity that can contain Fragments vs a plain Activity that predated the addition of fragments. One of my standard Android interview questions is to describe the differences between Activities and Fragments.
No, generics doesn’t help here. Generics once again only deals with compile time types.
There is no getting around that you will have an if-else tree, or a when statement for all cases or alternatively you can build a Map collection where the map key is a class object for the various types, which is what I did in my Dagger2 wrapper classes as generated by the Annotation Processor class I linked to earlier.
You can hide that code away somewhere, perhaps in this case making an of(Any) method that contains the when, but the code has to exist somewhere if you want to emulate double dispatch.
The other alternative if you have control of the object types of interest is to use the visitor pattern. That would not work for the OP.
Looks like I’m never going to get a job as an Android programmer. What a shame.
The use of generics here refers to using them on the right hand side of the ->. As you say, there’s no avoiding dealing with the types statically. Various hacks for handling otherwise static types dynamically are the same thing as the when cases at the end of the day.
Which leads us to the real problem here, which is (ab)use of the Any type. If you’re going to use it then you’re going to have to case things back into static types. Maybe the OP should be writing in JavaScript?
Strong typing is good for you, people don’t realise that.
I don’t think it is abuse at all. There are problems where this is very desirable, Dependency Injection being a major one.
Multiple dispatch can be a code smell, but in some cases simulating it is the only solution. The well known visitor pattern is a way to simulate multiple dispatch but only works if you own the code to the classes you want to simulate multiple dispatch on
Edit: corrected incorrect terminology of multiple dispatch vs. double dispatch
If there is an interaction with an object, shouldn’t an interface be defined and implemented? That’s type safe and efficient. The use of overloads and Any seem to all be inefficient and elaborate ways of achiving the same thing.
Correction: I corrected my earlier messages to get the terminology correct. The correct terminology is multiple dispatch instead of double dispatch. Double dispatch is a way to emulate multiple dispatch. The visitor pattern is an example of double dispatch.
Unfortunately real world problems do not fit into such nice, tidy theories. There are many reasons where that does not work or violates good design.
Imagine classic cases where the visitor pattern is often used. One simple case is a list of simple shapes. You would like to support drawing those shapes on different types of displays. It would be very bad design to include the code for displaying the shape on every type of display in the shape itself. That is very low cohesion and requires changing the shape class to add support for new displays.
So this is where a visitor pattern is often used. The first requirement for applying the visitor pattern is that it requires modifying the shape classes to add support for it. If you don’t control the source for those classes you can’t modify them so are out of luck from the start.
Secondly the visitor pattern requires adding boilerplate code to every class that it is used with. That code is typically a carbon copy of the code in every class. For the shapes example it may not seem like much, but for dependency injection I have seen people with a straight face say that you just add these same X lines to every class that you want to inject which when you multiply by hundreds of classes is quite a burden.
Also the visitor pattern introduces coupling between all of the classes that you can visit. They all have to accept the visitor interface and that visitor interface has to know about all of the shape classes. To see where this can be an issue imagine if the shape classes were distributed across multiple jars. You have one jar that supports basic shapes (rectangle, oval, etc.) and another jar that supports bezier curve shapes, and another jar that has convenient shapes for regular polygons. Designing a visitor pattern to encompass all these different shapes is very difficult while allowing some of the jars to be optional.
I am not saying that Kotlin should support multiple dispatch, but just that because it doesn’t problems like what the OP asked about require explicitly doing it yourself.
I’m not talking about “tidy theories,” I’m talking sensible, clean and simple design based on simple, cleanly designed language features. Your “tidy” design pattern (maybe better described as “lego blocks”) view of design isn’t convincing. You can have all the pre-cooked “designs” you like but if they require shoehorning into the language and implementation of the software, what’s the point? Why restrict yourself to them?
You’re just building up straw man arguments of how someone would write software with a few simple language features and at the same time suggesting elaborate language features to support your design pattern dogma.
Now the forum software is telling me I should try replying to other people! Wow, even the forum software is over it.
Not sure what straw man arguments I am supposedly making. All I have said is:
There are cases where you need to execute different methods based on the run time type of more than one value (which is called multiple dispatch)
Kotlin, like Java, only supports choosing methods based on the run-time type of a single value (via polymorphism), in other words single dispatch
Sometimes you can simulate multiple dispatch using multiple levels of single dispatch (e.g. double dispatch, the visitor pattern)
Sometimes you cannot or do not want to simulate multiple dispatch via double dispatch (e.g. you cannot change the classes or if it creates too much boilerplate overhead)
In that case to simulate multiple dispatch you have to do if-else, when, or a map
Not sure how you find any of that controversial or a straw man argument.
If you think that multiple dispatch (choosing a method based on the run-time type of more than one value) is never needed, the existence of the Visitor pattern and a little googling should easily disprove that.
Or perhaps you think that all cases of multiple dispatch can be done by multiple levels of single dispatch and that is definitely not the case.
Note that at no time did I say that one should strive to do multiple dispatch or that Kotlin should support multiple dispatch. If you thought I were saying that then you are reading that into it.
Multiple dispatch similar to the way CLOS does it would very nice to have in Kotlin. Sure, calling such functions would be slower than regular methods, since there must be extra code generated to resolves the actual function to call at runtime.
There have been plenty of times that I have wanted to have this, and the example shown in the original post looks very similar to code that I have in one of my Kotlin projects.
Computers are fast these days, and the programmer’s workaround to when multiple dispatch isn’t available is going to be just as slow.