Skip to main content

Makes switch() and case: work with any object or primitive

118 replies [Last post]

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
kcpeppe
Offline
Joined: 2003-06-15

JLS spec

patrikbeno
Offline
Joined: 2004-10-11

First, thanks a lot for deeper review :-)

(1)
[b]Fall-through[/b]
Now you may be a little surprised but I have to say what I enjoy about 'switch' is just this: fall-through capability.
You know, 'if/else' is always good for me in 1:1 relations [condition:action]. But I find less readable N:1 scenarios written in 'if/else'
For N:1 scenarios (typical if (or-or-or...) {dothis} ), I find switch more readable:
[code]
case A:
case B:
case C:
doThis();
break;
case D:
case E:
case F:
doThat();
break;
[/code]
It is better than following (and imagine there are 20 choices in total instead of 6):

if (A || B || C {
doThis();
} else if (D || E || F) {
doThat();
}

This is where fall-through is great. Everything is clean and readable and you can easily add another conditions.
For me, plain switch is fine but I can do that all using if/else without any readability issues. Don't you?

[b]Troublesome[/b] is M:N scenario (multiple conditions lead to multiple actions and actions intersect).
in this case people are often tempted to use fall through because many times it looks like I good idea.
[code]
switch (cond) {
case A:
doThis();
/* fall through */
case B:
case C:
doThat();
break;
...
...
}
[/code]
This looks quite readable from the begining but tends to become more and more complex and finaly you need to rewrite it.
Personally, I don't use this.

[b]Briefly:[/b]
Use 'if/else' if you have 1:1 condition:action relations,
use fall-through switch if you have N:1 relations as shown few paragraphs above.
For M:N relations, prefer if/else and do't get seduced by switch.

(2)
I cannot agree that [i]"the semantics of the new object-switch would be different of the semantics of the old primitive-switch"[/i]. Semantics is always the same; if condition matches, do action... Semantics for equality is defined in Java: == for primitives, equals() for objects. Nothing new here. Yes, I agree that primitive switch and object switch are implemented differently but [b]I have to insist that semantics does not change.[/b] It's always about equality and we do not redefine meaning of the equality.

However, you have a very [b]good point[/b] that [i]"Case's in the primitive-switch must be mutually incompatible"[/i] (exclusive).
I haven't realized this, I must admit.
This may be a serious problem since compiler just cannot evaluate nor enforce this. We'll have to consider this seriously.

Anyway, I think that mutual exclusivity requirement in current primitive switch is related to the way primitive switch is evaluated at runtime (mutual exclusivity is required by switch implementation). For object-switch, which would need to be implemented differently, this would not be applicable.

I agree this needs to be seriously considered. It needs deeper study and I don't have resources for this.
For now, I have to say I just think this can be worked out and that this mutual exclusivity issue is [b]not a show stopper[/b]. But we have to be careful. So thanks for the good point again.

Ad. your example:
> switch(whatever()) {
> case first: do1st(); break;
> case second: do2nd(); break;
> case third: do3rd(); break;
> }

This is so easy to figure out: it is ordinary 'if/else if/else' stuff. First match is executed. No misleading, nothing you can't figure out in a second. Sorry, at least I did not have a problem. I may have object-switch for a long time in my head, I don't know :-)

I don't see an issue here. It is pretty straightforward.

[3]
Now, I have an interesting and good news for you:
[b]switch works with enums[/b] :-)

[code]
enum Color {
RED, GREEN, BLUE
}
class SwitchTest {
Color c = Color.RED;
void test() {
switch (c) {
case RED: System.out.println("red!");
}
}
}
[/code]

I forgot about this. Notice that Enums are objects !! We already have switch that supports priviledged object types!
This is a precedent :-)
Why should we stop? Switch is guilty! It has to support all objects now! :-)

monika_krug
Offline
Joined: 2004-10-14

> I forgot about this. Notice that Enums are objects !!
> We already have switch that supports priviledged
> object types!
> This is a precedent :-)
> Why should we stop? Switch is guilty! It has to
> support all objects now! :-)

Are enum switches not actually switching on the int values?

Monika.

patrikbeno
Offline
Joined: 2004-10-11

That's not about what it's actually like! Enums are objects and that's the whole point. What's behind the scenes does not need to bother you if it works transparently and smoothly

monika_krug
Offline
Joined: 2004-10-14

But there are implementation and usage problems. Enums are unique and unchangeable automatically, they can be compared with ==, there are simply none of the problems of a general object switch.

Monika.

patrikbeno
Offline
Joined: 2004-10-11

Yes, I understand, I see your point. I see where the problem is. After all, object-switch cannot be implemented as primitive switch (using lookupswitch instruction)

But can you see my point? Enums are objects and they are used in 'switch' statement. That's all I wanted to point out. My point is all about syntax, nothing more.

kcpeppe
Offline
Joined: 2003-06-15

> No you are not. You use switch :-) This can be
> optimized as you know. compiler can do this. I just
> wanted to keep the example simple.
>
> But if you do not have enough imagination for this,
> here you go:
> [code]
> public void nonsense( MyObject a) {
> boolean eqWhatever = whatever.equals(a);
> boolean eqAnother = eqWhatever ||
> || anotherObject.equals(a);
> if (eqWhatever || eqAnother) {
> if (eqWhatever) {
> something();
> }
> somethingelse();
> }
> }
> [/code]
>
> See? if you hit 'whateverObject', there's one call to
> equals(). In case of anotherObject, there are two
> calls.

It's clear where a comes from.. the question is, where do these other objects come from?

>
>
> Message was edited by: patrikbeno

patrikbeno
Offline
Joined: 2004-10-11

> It's clear where a comes from.. the question is,
> where do these other objects come from?

And who cares? You just compare them with 'a'. Why do you ask? Do you see any problem I can't?

kcpeppe
Offline
Joined: 2003-06-15

> > It's clear where a comes from.. the question is,
> > where do these other objects come from?
>
> And who cares?

well.. imagine a class that is artifically constructed to hold onto a number of instances just for the purpose of acting as a trigger in a switch statement. For toy examples... it looks ok... but how about for half a dozen or so....

And, the second problem.. how do these objects get initialized.. and how do we ensure that they are initialized the same way such that we understand which state will trigger a particular clause in the switch statement.... how do I know by reading the code which clause will be called? The code may be compiled to equals but what does equals mean in this case? What state do I have to be in to trigger any one of the cases?
You just compare them with 'a'. Why do
> you ask? Do you see any problem I can't?

patrikbeno
Offline
Joined: 2004-10-11

I don't get your point.

I presented if/else example that is quite readable. Classic ordinary 'switch/case' statement makes this 'if/else' mess far more readable in many scenarios.

And that's whole point. no magic here. If you use 'switch' statement with ordinary objects in 'case' clauses, then this 'switch' statement is effectivelly expanded into that uncomfortable if/else mess. You are just not bothered.

So I still don't understand where the problem is. If 'if/else' works and none of your questions is a problem for it, why should it be a problem for 'switch/case' ?

Why do you continue to treat 'switch/case' as something so different? It is just condition/decision construct. Its implementation depends. Let the compiler do the job.

kcpeppe
Offline
Joined: 2003-06-15

> I don't get your point.
>
> I presented if/else example that is quite readable.
> Classic ordinary 'switch/case' statement makes this
> 'if/else' mess far more readable in many scenarios.
>
> And that's whole point. no magic here. If you use
> 'switch' statement with ordinary objects in 'case'
> clauses, then this 'switch' statement is effectivelly
> expanded into that uncomfortable if/else mess. You
> are just not bothered.
>
> So I still don't understand where the problem is. If
> 'if/else' works and none of your questions is a
> problem for it, why should it be a problem for
> 'switch/case' ?
>
> Why do you continue to treat 'switch/case' as
> something so different? It is just condition/decision
> construct. Its implementation depends. Let the
> compiler do the job.

The point is, it is making these decisions based on some construction. For primitives,the construction is quite apparent and quite readable. For objects, the construction is not so clear a definitly not so readable. For example, I cannot tell under what conditions either of the clauses in this simple example will be triggered. More over, I don't even know what or who has defined the objects used in the comparision. More over, have these object even been initialized before the switch statement has been executed and more over, how can the compiler in this situation tell you if they have NOT been initialized. Shall I go on? Or is it now clear that this may work with literials such as how primitives are represented but it is extremely problematic when you consider using it with objects.

It is most bizzare that people are making so many suggestions as to how Java maybe "improved" without really thinking through the consequences of the "improvement". This is why (as I stated before) these things need to attempted in a prototyping environment before they are released in the general population. I'm sorry to bring up generics again but this is but one example of a solution that may or may not have been needed but has not been given the proper workout to ensure that we developers get a consistant and sane implementation.

There are things that the language could use.. there are thing that the VM could use. For example, distrubuted support for singularity.. Annotations are interesting and they do solve some real problems but the current implementation is problematic (and this has been stated to me in persom by people that are on the expert committe for that particular JSR). Again, a longer development time would be beneficial to providing us with a sane implementation of injecting metadata into Java rather than hacking our way around and... ooops, we've made a mess and golly gosh... we can't roll back.

patrikbeno
Offline
Joined: 2004-10-11

I hope I am begining to see your point :-) Small and distant since it does not make much sense :-), but at least I see something :-)

> The point is, it is making these decisions based on
> some construction. For primitives,the construction is
> quite apparent and quite readable. For objects, the
> construction is not so clear a definitly not so
> readable.

(1) Have you ever seen compiler error "Variable XY may not have been initialized" (or alike) ?
In 'case' clauses you use references to objects. All such objects must have been initialized before they are referenced.

(2) Any 'switch/case' statement can be rewritten into some kind of 'if/else' statement. And it will work. everything else is just a matter of the specification of how object-enabled 'switch/case' statements are converted into 'if/else' statements

We may discuss this since there are better as well as worse solutions.
But [b]it is feasible[/b].

> For example, I cannot tell under what
> conditions either of the clauses in this simple
> example will be triggered.

No you don't because it was not yet specified. once it is specified, you WILL be able to tell and there will be no problem. These feature proposals are primarily about WHAT we want to achieve (syntax, in this case). Once we decide it is a good and useful idea, we may analyse HOW we want to achieve this (specification, in this case).

> More over, I don't even
> know what or who has defined the objects used in the
> comparision.

You don't care. Objects are able to compare with each other, that's what equals() is for. Contract is well defined.

> More over, have these object even been
> initialized before the switch statement has been
> executed and more over, how can the compiler in this
> situation tell you if they have NOT been initialized.

??? compiler generates byte code according to well defined rules and believe me, it WILL be able to tell it to you. Exactly as it does today. It just depends on [b]the rules[/b] which we did not specify yet.

> Shall I go on? Or is it now clear that this may work
> with literials such as how primitives are represented
> but it is extremely problematic when you consider
> using it with objects.

Again, it is not more extreme nor more problematic the plain old 'if/else' since you can rewrite every 'switch/case' into 'if/else'. All you need is to know the rules.

> It is most bizzare that people are making so many
> suggestions as to how Java maybe "improved" without
> really thinking through the consequences of the
> "improvement".

Sorry but I DO think of consequences. A lot. Maybe I don't see everything but I try.

> This is why (as I stated before) these
> things need to attempted in a prototyping environment
> before they are released in the general population.

Agreed but if you say +1 to any feature in this forum it does not mean that it will be blindly implemented in Mustang. [b]These are all just proposals[/b] and may or may not be further evaluated. Expert committees will be established, Some features will get into Mustang, others may be deferred into future release, many of them may be rejected, etc etc.
But where do you want to start these kind of things if not here?

> I'm sorry to bring up generics again but this is but
> one example of a solution that may or may not have
> been needed but ...

This is yet another useful syntactic sugar. I coded quite a lot of 'if/else' horrible mess that could be far more readable as 'switch/case' statement. I know what I am saying.
And please don't start about polymorphism and pure OOP architecture. Sometimes (more or less often) you don't have a choice but conform to the existing architecture.

> distrubuted support for singularity.. Annotations are
> interesting and they do solve some real problems but
> the current implementation is problematic (and this

I am interested. Very. Annotations are something that I will use really heavily in the near future so I would appreciate ANY opinions.
But this is not the right place. You may choose to open a new thread or mailto: patrik.beno@zmail.sk.
THANK YOU :-)

> Again,
> a longer development time would be beneficial to
> providing us with a sane implementation of injecting
> metadata into Java rather than hacking our way around
> and... ooops, we've made a mess and golly gosh... we
> can't roll back.

Agreed. But in case of generics it is not matter of development time. It was just poor design decision.
As for annotations, I am eagerly waiting for more details from you :-)

cheers

kcpeppe
Offline
Joined: 2003-06-15

> I hope I am begining to see your point :-) Small and
> distant since it does not make much sense :-), but at
> least I see something :-)
>
> > The point is, it is making these decisions based
> on
> > some construction. For primitives,the construction
> is
> > quite apparent and quite readable. For objects,
> the
> > construction is not so clear a definitly not so
> > readable.
>
> (1) Have you ever seen compiler error "Variable XY
> may not have been initialized" (or alike) ?
> In 'case' clauses you use references to objects. All
> such objects must have been initialized before they
> are referenced.

only for local variables and in most cases.. it is a broken error because the compiler cannot properly figure out that you actualy have initialized the variable.

>
> (2) Any 'switch/case' statement can be rewritten into
> some kind of 'if/else' statement. And it will work.
> everything else is just a matter of the specification
> of how object-enabled 'switch/case' statements are
> converted into 'if/else' statements

you are of course ignoring polymorphisum.. the better way to do this... with polymorphisum.. there is no evaluation.. just a plain old method call.. To simple for you?

>
> We may discuss this since there are better as well as
> worse solutions.
> But [b]it is feasible[/b].
>
> > For example, I cannot tell under what
> > conditions either of the clauses in this simple
> > example will be triggered.
>
> No you don't because it was not yet specified.

Right and that doesn't come till runtime.. or maybe it's defined somewhere else in the code that initialized the objects.. this.. does not improve readability at all.

>
> You don't care. Objects are able to compare with each
> other, that's what equals() is for. Contract is well
> defined.

Sorry but I do care because when I'm reading code, I need to understand flow.. a method call is simple.. this is now a complex flow control structure will and ill defined set of rules.

> > situation tell you if they have NOT been
> initialized.

does it? as I said... the current compiler can't figure this out in simple situations much less the complex one that this proposal is suggesting...

>
> Again, it is not more extreme nor more problematic
> the plain old 'if/else' since you can rewrite every
> 'switch/case' into 'if/else'. All you need is to know
> the rules.

I guess I just don't hardly ever use a switch statement. As a mater of fact, as I grep my code for switch statements.. I find 1 (in 10s of thousands of lines of code).

>
> > This is why (as I stated before) these
> > things need to attempted in a prototyping
> environment
> > before they are released in the general
> population.
>
> Agreed but if you say +1 to any feature in this forum
> it does not mean that it will be blindly implemented
> in Mustang.

Thank god for that because there are quite a number of hairbrained ideas floating around in here (as well as a number of good ones). Try reading http://epesh.blog-city.com (and no, it's not me ;))

> far more readable as 'switch/case' statement. I know
> what I am saying.

So, we are at opposite ends of the specturm then.

As for Annotations.. seek out the words of the people in that JSR.

One question to ask yourself.. what happens if you get a framework that has annotations that reference a commerical package that you don't have a license for?

patrikbeno
Offline
Joined: 2004-10-11

> > (1) Have you ever seen compiler error "Variable XY
> > may not have been initialized" (or alike) ?
> > In 'case' clauses you use references to objects.
> All
> > such objects must have been initialized before
> they
> > are referenced.
>
> only for local variables and in most cases.. it is a
> broken error because the compiler cannot properly
> figure out that you actualy have initialized the
> variable.

Of course only for local variable because other variables are initialized by default to NULL. So says JLS.
And plase give me an example where compiler cannot figure out uninitialized variable. Because I never found one. (Speaking about local variables because of what I said above)

>
> you are of course ignoring polymorphisum.. the better
> way to do this... with polymorphisum.. there is no
> evaluation.. just a plain old method call.. To simple
> for you?

plese don't start again. This thread is not about it, it is about object support in 'switch'.
if you want 'switch' to be removed completely from the language, no matter whether it is feasible or not, start a new thread, I will gladly discuss it there.

> > No you don't because it was not yet specified.
>
> Right and that doesn't come till runtime.. or maybe
> it's defined somewhere else in the code that
> initialized the objects

i meant, if you can simply convert 'switch' to 'if/else', it will be only as complex and unreadable as the resulting if/else code

>.. this.. does not improve
> readability at all.
> ...
> Sorry but I do care because when I'm reading code, I
> need to understand flow.. a method call is simple..
> this is now a complex flow control structure will and
> ill defined set of rules.

It can be made very simple and readable, consider this (not that I am proposing this, it is just an example):

- before the whole 'switch' is evaluated, expression in switch() is evaluated, than every expression in every 'case' clause is evaluated (objects are initialized)
- each equals() comparison is performed at most once and in the order of the 'case' clasuses.

Simple and readable enough?

> > Again, it is not more extreme nor more problematic
> > the plain old 'if/else' since you can rewrite
> every
> > 'switch/case' into 'if/else'. All you need is to
> know
> > the rules.
>
> I guess I just don't hardly ever use a switch
> statement. As a mater of fact, as I grep my code for
> switch statements.. I find 1 (in 10s of thousands of
> lines of code).

Seems like 'switch' is not area of your expertise, you barely use it. No wonder you just cannot understand what this proposal is about.

You cannot do everything effectively with polymorphism and OOP, it is not cure for all diseases. So don't say you can, you can, you can... I asked for example but all you do is ... talk.

> So, we are at opposite ends of the specturm then.

I just use standard old style flow control mechanisms when I have to or when I decide that polymorphism and OOP is just too heavyweight armoury for what I need to achieve.

And I'd love to see examples how can I avoid it. Show me.

> One question to ask yourself.. what happens if you
> get a framework that has annotations that reference a
> commerical package that you don't have a license for?

1) This should not happen. Annotations are just annotations, but their use is in the code that interperts/handles them. So annotations should always be free because they alone do nothing. Licensing is aplicable only for the product that processes those annotations. but this depends on license policy, of course.

2) Anyway, if I want such a framework, i will have to obtain license. or pick another framework...

3) Still, the one who distributes the framework should distribute all I need to make use of it.

Anyway, what's the point? This is the only wrong thing on annotations?
You mean that if those annotations were in javadoc comments thay would not conform to the licensing policy?

jsando
Offline
Joined: 2003-06-22

>
> Are you are trolling????
>

Whoa there, friend. Perhaps I did use some 'charged' language but it was unintentional. I do get frustrated sometimes when regular programming tasks that should be simple take extra work. Microsoft has been more responsive on many such issues, but I'll admit they often shoot themselves in the foot.

My comment about using maps came from a system interface. I believe I did create separate classes for each record type coming in, but to figure out which type to create I had to test a String against a set of values. So after using the switch, from then on I could use polymorphism.

Another example I encounter daily is with Swing actionListeners. I don't like anonymous inner classes, and Actions are often overkill. If a screen has 8 buttons on it, a switch() would be a nice readable way to dispatch to the event handlers.

kcpeppe
Offline
Joined: 2003-06-15

> >
> > Are you are trolling????
> >
>
> Whoa there, friend. Perhaps I did use some 'charged'
> language but it was unintentional.

The comment was not directed towards you :)
>
> Another example I encounter daily is with Swing
> actionListeners. I don't like anonymous inner
> classes, and Actions are often overkill.

Yes, it is unfortunate how Swing has implemented events, sort of a carry over from the AWT. But should we really be changing the language to overcome a less then exemplary design in a library?

jwenting
Offline
Joined: 2003-12-02

> If what I read was correct, Visual Age for Java was
> implemented in Smalltalk. And there are many

I'd say that's a good reason to not use SmallTalk... VAJ is one of the worst products I ever had the displeasure of having to use (the worst ever being another IBM product, Lotus Notes).

kcpeppe
Offline
Joined: 2003-06-15

> I'd say that's a good reason to not use SmallTalk...
> VAJ is one of the worst products I ever had the
> displeasure of having to use

Not Smalltalks fault.. there are some very nice application written in Smalltalk.

dog
Offline
Joined: 2003-08-22

> These languages are just a toys.
> not for "industrial" software development.

Ok you went too far!

Smalltalk is not a toy!!
Large companies pay VisualWorks, IBM and others $5000/developer for licenses of their Smalltalk products. Back in 1994 I remember seeing job offers for senior Smalltalk programmers that paid $300K a year.

The only reason (IMHO) that Smalltalk doesn't rule the world is that people still think of it as a 1980s language and only evaluate the retro versions of the language (and not some of its more modern, more standard LoFs like IBM Smalltalk). At the time (c. 1980) it was: an interpreted, graphically oriented, garbage collected system. People thought you had to be insane to bog down your system with that stuff back then.. in fact, programmers weren't sure high level languages were a good thing back then. Then we spent the next two decades reinventing what Smalltalk had in 1980.. in many ways we aren't there yet.

Let me see.. a quick list of what we copy from Smalltalk in Java and OO programming:

- the way references are handled
- GUI programming was pioneered and many ideas still apply
- The mouse was first supported in Smalltalk
- eXtreme Programming was developed from Smalltalk
- the GoF book "Design Patterns" was written by Smalltalkers based on their experiences.. In 1994 I remember seeing "builders", "factories", and other design patterns when those of us focused on C++ where still debating "virtual"
- Byte code interpreters
- Virtual Machine
- Swing copies the old PARC idea of reimplementing the work because Xerox thought of Smalltalk as the operating system.. not just a language
- Refactoring
- SWT is an obvious copy of the GUI libs of OTI Smalltalk (yes.. OTI was primarily a Smalltalk company!!)
- A lot of IDE features like autocorrecting, incremental building, etc.. were pioneered in Smalltalk environments
- Python, Ruby and even Java are just lame versions of Smalltalk for C++ brainwashed masses (ok.. maybe a bit too strong: there are innovations in these languages that Smalltalk does not have)
- Javadoc functionality
- Reflection
- Who do you think had to invent all the sophisticated garbage collection algorithms Java uses today? And theirs worked on much smaller hardware than the stuff we have. (Lisp also contributed to this, but Smalltalk was used a lot more in the commercial world)
(I'm not even a Smalltalk expert and see how much stuff I can just spew out off the top of my head)

Some thing Java programmers don't miss because they don't know true bliss yet:
- Interactive debugging: where you hit a bug.. drop to the appropiate stack frame, change the code, and continue from where the program left off (lets see you do THAT in Java/Ruby/Python/Perl/C++/whatever)
- Extremely sophisticated configuration management (note VisualAge for Smalltalk existed before 1994.. waaay before Java came around and VisualAge for Java was built)
- Proper class methods.. sheesh.. this "static" junk is just pathetic
- A non-flat view of you code
- Code managed in a database which you can query
- Extreme elegant SIMPLICITY
- Simple code generation (lets concatenate these strings, run it through the compiler class and bingo.. we have a new class)

The new languages/tools have many things Smalltalk didn't have.. but they still lack some of the true powerful tools that Smalltalk had.

I often look back at Smalltalk and wonder why we haven't supported it more.. Could it be because of the oddball UIs running around (like Squeak)? IBM Smalltalk is a lot more "normal" and thus more acceptable to the masses.

Whenever I look at cool Python or Ruby features I say: "cool its just like Smalltalk". (In fact, that is what attracted me to Java) Technically speaking Java has NO advantage over Smalltalk (except maybe the fact that it is statically typed.. which is a debatable "advantage").

Alan Kay didn't win the Turing award just because he's a nice guy you know. Smalltalk is just about the most influential language in existance today..

Not a toy language by any stretch. Just too much vision for its time (maybe even today!).

dog
Offline
Joined: 2003-08-22

sorry about the rant.. but it bugs me when people say "toy language" and then spend the next 20 years trying to do what it can do... better.

saintjohn
Offline
Joined: 2004-10-25

Will we ever have a pure OO language? :)

monika_krug
Offline
Joined: 2004-10-14

Smalltalk? Ruby?

Monika.

saintjohn
Offline
Joined: 2004-10-25

These languages are just a toys.
not for "industrial" software development.

vhi
Offline
Joined: 2004-10-11

If what I read was correct, Visual Age for Java was implemented in Smalltalk. And there are many commercial products that were implemented in Smalltalk.

kcpeppe
Offline
Joined: 2003-06-15

> If what I read was correct, Visual Age for Java was
> implemented in Smalltalk. And there are many
> commercial products that were implemented in
> Smalltalk.

You read that correctly. And, Smalltalk is still used in many industrial settings today.

The fact that Smalltalk looks so different than the traditional languges that we are somewhat brainwashed to accept as being normal is what IMHO, helped undermine the growth that it was enjoying in the mid-90s.

It is too bad that people have been "trained" to accept certian language features as being necessary in order for them to be productive. IMHO, the a number of the current set of new langauge features in Java are code smells. Unfortunately, the standard solutions have been applied, lets address the symptom by adding more stuff. The problem with this approach is that more stuff generally leads to more/different) problems. It is only when we actually really work in other languages (simply put; writing C like code in Java) that we open our selves up to new ideas of working.

Take the covarient return types example that was published in the core java technology tips. It is a simple idea; the ability to declare a different return value for method. I've run into a few situation where I myself have said, darn, I'd really like to be able to return this object or that object. Since Smalltalk doesn't inforce type'ing, I could easily do this in that language and I did run into situations when coding in Smalltalk where I would be thinking, I need to return this object of that object. Fortunately, I followed good engineering fundimentials and those kept me from ever having a method return two different types. In Smalltalk, the lack of declared type'ing didn't eliminate the need to respect type'ing. Coding in Smalltalk was always a problem for those that didn't understand this. I'm not really for or against covarient return types. That said, I've seen the problems that inexperienced developers can run into when they use this type of feature. I don't think we can enforce (nor am I sure I would want to) "good coding practices" with a compiler. What I prefer is simplicity.

I have never missed the lack of a switch statement in Smalltalk. The only reason for it in Java is that I can use polymorphisum on primitive types. If I could use polymorphisum, I don't think I'd ever use a switch statement. They are cumbersome and violate a number of coding and OO prinicples. It is even unclear how they should work and what effect that they'd have on performance if used in Java. The semantics of switch are orthogonal to deligation (or a message send). Why not let the object make the decision as it will have the context to do so. Is is the same reason that continously run into code that uses chained getters? Why are people running away from deligation?

gmanwani
Offline
Joined: 2004-07-12

There is an enhancement request currently open: -

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5012262

rwentworth
Offline
Joined: 2005-05-17

The problem with making switches work with any object is the truly constant nature of the switch statement. To use object in a switch case a constant interface needs to be defined for them. One way to do this is to make a constant int or char value part of your object. Another way that will work for any object type is to use a Hashtable. I'm including a way of doing the last method.

public class SomeClass {
public static final int ATR = 0;
public static final int BPS = 1;
public static final int DTR = 2;
public static final int EPS = 3;
public static final int FAR = 4;
public static final int FTR = 5;
public Hashtable recordTypes= new Hashtable(6);
public int getType(Object rec) {
return ((Integer)recordTypes.get(rec.getClass().getName())).intValue();
}
public static void main(String[] args) {
//Add a record for each object I use the class name
recordTypes.put("ATRRecord",new Integer(0));
recordTypes.put("BPSRecord",new Integer(1));
recordTypes.put("DTRRecord",new Integer(2));
recordTypes.put("EPSRecord",new Integer(3));
recordTypes.put("FARRecord",new Integer(4));
recordTypes.put("FTRRecord",new Integer(5));
Object rec=/* process objects */

switch(getType(rec)) {
case (ATR):
/*code*/
break;
case (BPS):
/*code*/
break;
case (DTR):
/*code*/
break;
case (EPS):
/*code*/
break;
case (FAR):
/*code*/
break;
case (FTR):
/*code*/
break;
case default:
/*code*/
break;
}
}
}

Using a hash table let's us use an indexed environment this prevents the looping cause by most lookup methods in our case we gained 1000%+ increase in performance over the nested if method.

tackline
Offline
Joined: 2003-06-19

As a workaround, it is possible to fake a switch on strings (and other objects). Simply define an enum type with names matching each string, then look up the enum to switch on with valueOf. For awkward objects, give the enum an attribute and create a mapping from that. Example code on my weblog.

monika_krug
Offline
Joined: 2004-10-14

Oh, good idea. :-)

Monika.

yorkpaddy
Offline
Joined: 2004-12-01

I can think of a place it would be more useful

take this example
switch (shapes.get(i).getClass) {
case Myline.class: doLineStuff(); break;
case MyCircle.class: doCircleStuff(); break;
default: setBackground(WHITE); break;
}

I find switch/case easier to read then if/else

The performace aspects seem negligible on either side, but the readability aspects would be huge if implemented correctly.

brucechapman
Offline
Joined: 2004-03-18

This is getting close to the situation where I would appreciate an enhanced switch.

In particular I would like a construct that branches on instance type and refines the type inside the construct.[code]MyShape shape = shapes.get(i);
switch(shape instanceof ?) {
case MyLine:
/* in here shape is known to be of type MyLine
no additional variable to define, or cast, ie
implicit "final MyLine shape = (MyLine)shape" here
*/
shape.doLineStuff();
break;
case MyCircle:
shape.doCircleStuff();
}[/code]Using fallthrough (omitting break) from case A to case B would be a compiler error unless B is a supertype of A.

LHS of instanceof would have to be a variable.

Of course someone ( an OO purist) might be able to show me how to do this now without using the if based idiom with all its repetition[code]if(r instanceof X) {
X rdash = (X) r;
...
} elseif ( r instanceof Y) {
Y rdash = (Y} r;
...
}[/code]

kcpeppe
Offline
Joined: 2003-06-15

Humm, an OO purist might say that the the circle in doCircleStuff() and the the line in doLineStuff() was were sort of redundent and that doStuff() would to the trick. Now all we are left with is

Shape shape
......
shape.doStuff();

might do the trick ;)

brucechapman
Offline
Joined: 2004-03-18

Ah,

But that assumes that the author(s) of MyCircle and MyLine anticipated exactly what I wanted to do, and made that function available as implementations of the same (possibly abstract) doStuff method in MyShape.

While this could be true If I am the author of them all, it is unlikely if I am using a class heirarchy that I did not write.

As an example, say I needed the X coordinate of the point on the shape that was closest to the X axis, (or the mean of all of them if there was more than 1)

For the circle this could be getCentre().getX() and for the line getXIntercept(). It is highly unlikely that this particular requirement was anticipated by the author of the superclass.

In practice when using external libraries, you often want to perform an operation which is not defined in the superclass, and whose implementation cannot be composed from superclass methods in such a way that is appropriate for all subclasses. You need to write more than one implementation yourself and choose which one according to the type of the instance.

kcpeppe
Offline
Joined: 2003-06-15

> Ah,
>
> But that assumes that the author(s) of MyCircle and
> MyLine anticipated exactly what I wanted to do

To be fair, I think it assume that the authors of MyCircle and MyLine normalized thier Shape behavor interfaces and since you indicated that these were shapes that were being dealt with, then I did assume that this occured with these shapes were defined.

Now that said, maybe they didn't want to implement something like Shape.intercepts( Shape); in which case, you do have a point that. Certianly there are plenty of cases in AWT (and other points in the code) where the authors ignored double dispatch and other like patterns that often clear up these types of coding problems in a hurry and actually force you to propogate the less than optimal style. Does this mean we should further change the language to accomidate this. Difficult question and one that I hesitate to say yes to.

jsando
Offline
Joined: 2003-06-22

I have this partially implemented in the javac compiler released under the JRL. You can download the sources (including a build file) from http://javacresearch.dev.java.net

Select "Documents and Files", then download the bundle under 'obj-switch'.

The bundle includes some docs and an ant buildfile.

This is very experimental, obviously!

yod
Offline
Joined: 2004-10-20

Ok, let's have a deeper insight on what all this stuff of object-switch means.

First of all, I would like to remark that I like a broader switch, but I disagree with a switch that could be an absolute synonymous of a nested if/else. So, in the end of this post I give an alternative for an object-switch that I would really love.

Firstly: switch is inherently evil ;) I mean, you would have heard all those things about [b]structured programming[/b] and why goto's are the devil and control transfers are not good and etc. Switch is not a basic control structure. So, should we discard it at all? No, I don't think so, but it should be used "with moderation" ;). When somebody codes a broken loop (with break's and continue's and labels...), it could look nice to the one who coded it; however -and I have really disgusting experiences with that- when somebody else reads that code, it is much more likely to get lost around that damned "break" or "continue" statement.

All right... how does it all relate with switch? The answer is in the [b]fall-through's[/b]. A plain switch (case / break, case / break...) is nice, it's like a real train switch. But a switch that is infested with falls-through's becomes a bit messy. So:
- I prefer writing a switch when it is a [b]plain switch[/b], a choice type like one-out-of-n. I use it as a kind of [b]multiple valued condition[/b]. I mean, I use an if/else when it is a boolean condition and a switch when I have more than two options.
- I prefer writing a nested if/else when things are not so simple. Code gets longer, it's true. But it is [b]more linear[/b] so, unless you have a very clear wiew about the code, it is easier to follow (keep in mind that you may have a clear view, but others who will read your code surely do not have it).

Next thing: [b]equals[/b] method. Until now, I have said that I prefer a switch when I have a 1-out-of-n option, a multiple choice without falls-through's. So, ok, you would ask me why we should not use an object-switch just in that case (and you would say that if developers use it in a messy way, it is their fault, as it happens with the current primitive-switch). The problem lies with equals. Equals comparison is too bug-prone. If you see a.equals(b) you are aware that you are calling that method and that it could be broken somewhere, in some place, just perhaps, in some hidden subsubsubclass. If you use a syntactic sugar hiding that "equals", bugs could slip without noticing that.
I realize that we have currently another example where this could happen: the new foreach. When an Iterable object doesn't return a working Iterator, we could be in trouble. Imagine an Iterable that returns an Iterator that never ends... the loop would become infinite. But my experience says that it is much easier to find a broken equals than a broken hasNext/next.

And now, last but not least: [b]the semantics of the new object-switch would be different of the semantics of the old primitive-switch.[/b] I think this is in its own a cause to reject it. Why? Because of three reasons:
1. Primitive-switch uses == (reference comparison) whereas [b]object-switch would use equals[/b] (yeah, equals keeps here around). Syntax overriding is VERY dangerous, since human mind is used to induction and tends to think that things which look the same are the same. Believe me, people would tend to think that object-switch also uses ==, even if they think it in an inconscious way.
2. Case's in the primitive-switch must be [b]mutually incompatible[/b]. And in the object-switch you propose, this could not be guaranteed (since the compiler could not know if an equals method will succeed or fail). This is a deep change in switch semantics.
3. And, following 2, that would imply that code flow would become harder to follow, since [b]you would not know if a case will succeed without knowing first what has happened with the previous cases[/b]. That is, in the next code (suppose object-switch):
[b]switch(whatever()) {
case first: do1st(); break;
case second: do2nd(); break;
case third: do3rd(); break;
}[/b]
you could not assume that do3rd() is executed if whatever() satisfies case third, because whatever() could also have previously satisfied case first or second, and do3rd() would have never been executed. Maybe you don't see the problem in this and, in fact, there is no problem [i]if you are able to keep in mind that object-switch behaves in that way[/i]. The point is that programmers would tend to think about the old switch (as I have said, syntax overriding is very dangerous). A nested if/else could be much less misleading. Ok, deep nesting is an indicator of poor code because of its lack of legibility but, if you really need it, a nested if/else would be much more legible than an ambiguous switch (I say 'ambiguous' because of the differences with the old primitive-switch).

And now... the good news. I have missed an object switch some times, many times. But what I have really missed is a [b]reference-switch. I would restrict case values to compile-time constants that would be compared by reference (ie, with == and not with equals) with mutually incompatible cases (otherwise, a compiler error should raise).[/b] It avoids all the problems I have quoted above and it solves the problem in places where a switch is intended to behave like a classic switch (remember, I often think about a train switch :) ).

I would go one step further: [b]switch with enums[/b]. If my reference-switch proposal (remember, same semantics as the primitive-switch) were not be regarded as advisable, I would never drop off a switch with enums. We already have typesafe enums, it's much better than C but... we have lost switch with enums... Let's get it now! Types that have a finite number of possible, easily distinguishable and mutually excluding values are great candidates to be used in a switch (ie, integer-valued primitives and now also enums). Indeed, a clever compiler could even detect in an enum-switch when a default clause is not reachable (because all enum values would have matched a case).

Well, I think my brain is warm enough for today, what do you all think about it?

callen982
Offline
Joined: 2004-01-14

> I would go one step further: [b]switch with
> enums[/b].

Actually, one [i]can[/i] switch on an enum.

yod
Offline
Joined: 2004-10-20

I think it is related with the way the compilers deal with a switch. If my old-assembly-knowdledge does not fail, a switch (with a primitive type) may be evaluated in runtime in constant time, whereas an if/then/else is evaluated in O(n).

kcpeppe
Offline
Joined: 2003-06-15

> I think it is related with the way the compilers deal
> with a switch. If my old-assembly-knowdledge does not
> fail, a switch (with a primitive type) may be
> evaluated in runtime in constant time

The switch statement is support with two different byte code. One does a calculated goto based on the case statements being a contiguous set of ordinal values. The second does a O(n) search and is used when there is a break in the sequence.

, whereas an
> if/then/else is evaluated in O(n).

if then else statments are supported by block structures and as such are supported by a constantant time goto like construct.

yod
Offline
Joined: 2004-10-20

> if then else statments are supported by block
> structures and as such are supported by a constantant
> time goto like construct.

I meant that if/then/else nested structures are evaluated in O(n), where n is the depth of the nesting (supposed that all branches are equally probable). A single if then else statement is evaluated in constant time, of course.

Anyway, you have answered to what I was saying: both switch (with break's as it is usually done) and if/then/else carry O(n) times, so I was wrong.

kcpeppe
Offline
Joined: 2003-06-15

> > if then else statments are supported by block
> > structures and as such are supported by a
> constantant
> > time goto like construct.
>
> I meant that if/then/else nested structures are
> evaluated in O(n), where n is the depth of the
> nesting (supposed that all branches are equally
> probable). A single if then else statement is
> evaluated in constant time, of course.
>
> Anyway, you have answered to what I was saying: both
> switch (with break's as it is usually done) and
> if/then/else carry O(n) times, so I was wrong.

I was a bit surprised to learn that "broken" switch statement is not done as a calculated goto or as a table lookup. I guess having it this way is a bit less complex. FWIW, you can get some interesting micro performance improvements by artifically making your case statements contigous. Not that I'm keen on writting code for reasons of micro-performance unless it counts ;)

jsando
Offline
Joined: 2003-06-22

I've been thinking for 8 years that its just silly that this has not been added. And all you fellas discussing better ways to implement code have great ideas, but Sun should not be dictating "the one right way" to all of us. Its a minor change to allow any object type in a switch, and would clean up all the times I've had to build a map to delegate to code based on a large set of string values.

This would be a solid, useful enhancement.

kcpeppe
Offline
Joined: 2003-06-15

> I've been thinking for 8 years that its just silly
> that this has not been added. And all you fellas
> discussing better ways to implement code have great
> ideas, but Sun should not be dictating "the one right
> way" to all of us. Its a minor change to allow any
> object type in a switch, and would clean up all the
> times I've had to build a map to delegate to code
> based on a large set of string values.
>
> This would be a solid, useful enhancement.

Are you are trolling????

1) A string... is a string that exhibits string behavior. If you desire another behavior, then perhaps, you don't have a string.
2) if you want the OO equivilant of a switch statement, it's called polymorphisum which means you need to recognize the content in statement 1. Interfaces help a lot in this regard.
3) how would one "switch" on a String or any other complex value is not clear nor has anyone here made it clear how it might work because.. it's nonsensical... I fail to see how this could be made as a simple change.
4) If you've been building maps to delegate behavior then perhaps you should look at the command pattern and point 1 and 2. The proxy pattern might also offer some assistance.

patrikbeno
Offline
Joined: 2004-10-11

Following:
[code]
String s = ...;
switch (s) {
case "maybe" : doMaybe(); /* and fallthrough to yes */
case "yes" : doYes(); break;
case "no" : doNo(); break;
default : notSupported(); break;
}
[/code]

can be rewritten as follows:
[code]
String s = ...;
if ("maybe".equals(s) || "yes".equals(s)) {
if ("maybe".equals(s)) {
doMaybe();
}
doYes();
} else if ("no".equals(s)) {
doNo();
} else {
notSupported();
}
[/code]

it can be rewritten to a more effective if/else implementation. but it's not the point.

Point is that object-enabled 'switches' can be compiled to if/then/else equivalent. Only that sometimes 'switches' are more flexible than if/else, as obious from the examples above

kcpeppe
Offline
Joined: 2003-06-15

> Following:
> [code]
> String s = ...;
> switch (s) {
> case "maybe" : doMaybe(); /* and fallthrough to
> to yes */
> case "yes" : doYes(); break;
> case "no" : doNo(); break;
> default : notSupported(); break;
> }
> [/code]
>
>
> it can be rewritten to a more effective if/else
> implementation. but it's not the point.

*blinks*

kcpeppe
Offline
Joined: 2003-06-15

> > Following:
> > [code]
> > String s = ...;
> > switch (s) {
> > case "maybe" : doMaybe(); /* and fallthrough to
> > to yes */
> > case "yes" : doYes(); break;
> > case "no" : doNo(); break;
> > default : notSupported(); break;
> > }
> > [/code]

ok, lets frame with discussion with another example

[code]

public void nonsense( MyObject a) {

switch( a) {

case what goes here??? : something();
case what goes here??? : somethingelse();
}
[/code]

so, what does go there?

patrikbeno
Offline
Joined: 2004-10-11

What does go there? Anything! An object!

[code]
public void nonsense( MyObject a) {
switch( a) {
case [u]whateverObject[/u] : something();
case [u]anotherObject[/u] : somethingelse();
}
}
[/code]
and compiler does this:
[code]
public void nonsense( MyObject a) {
if (whateverObject.equals(a) || anotherObject.equals(a)) {
if (whateverObject.equals(a)) {
something();
}
somethingelse();
}
}
[/code]

kcpeppe
Offline
Joined: 2003-06-15

> What does go there? Anything! An object!
>
> [code]
> public void nonsense( MyObject a) {
> switch( a) {
> case [u]whateverObject[/u] : something();
> case [u]anotherObject[/u] :
> t[/u] : somethingelse();
> }
> }
> [/code]
> and compiler does this:
> [code]
> public void nonsense( MyObject a) {
> if (whateverObject.equals(a) ||
> || anotherObject.equals(a)) {
> if (whateverObject.equals(a)) {
> something();
> }
> somethingelse();
> }
> }
> [/code]

So, I'm going to do three calls to equals for this?

patrikbeno
Offline
Joined: 2004-10-11

No you are not. You use switch :-) This can be optimized as you know. compiler can do this. I just wanted to keep the example simple.

But if you do not have enough imagination for this, here you go:
[code]
public void nonsense( MyObject a) {
boolean eqWhatever = whatever.equals(a);
boolean eqAnother = eqWhatever || anotherObject.equals(a);
if (eqWhatever || eqAnother) {
if (eqWhatever) {
something();
}
somethingelse();
}
}
[/code]

See? if you hit 'whateverObject', there's one call to equals(). In case of anotherObject, there are two calls.

Message was edited by: patrikbeno

kcpeppe
Offline
Joined: 2003-06-15

>
> See? if you hit 'whateverObject', there's one call to
> equals(). In case of anotherObject, there are two
> calls.

so, 1 + 2 = 3..... great thank you for checking my arithmatic!!!