Duplicate :
|
Background ---------- Java 5 introduced generic types to the core Java syntax. This has had two effects. Firstly, the type-safety of code using generics is much better. In particular, it is now possible to have type-safe generic collections. Seccondly, it has removed the need for many explicit cast operations. This makes the code more readable, and also cuts out yet another source of run-time failures. Some casting between Object types will always be required in Java. However, generics have shown that removing the need for explicit casts by adding language features can be a worth-while trade off between language complexity and increased code quality. One of the most frequent remaining cases where casts are used is when executing a block only if an object is of a given type. A contrived example would be: Object item; ... if(item instanceof String) { String s = (String) item; s.subString(2, 5); } else if(item instanceof Number) { Number n = (Number) item; n.intValue(); } else ... Code like this is often found in parsers and AST processors, serialization code, and user input handling. Although sometimes it indicates that the software has not been developed in a very OO manner, it is in some cases unavoidable or just pragmatic. These code blocks always follows the same highly-stylised form. There is an if/else where each if is a test on instanceof for some class, and then inside the if, the object is cast to that class and then used. C# and C++ both have syntaxes to simplify this type of conditional-on-type expression. ML-family languages tend to support this kind of expression directly in the language as a list of alternates guarded by types using their pattern-matching facilities. Some languages, such as NICE use multiple dispatch (Java has single-dispatch - dispatch on object type, and non-oo procedural langauges usually have just one method for each identifier). The classical OO approach to working arround having single dispatch is to use the Visitor design pattern, but this comes with design-time overheads and can't always be retro-fitted to existing code. The proposal ------------ The switch construct allows code to be conditionaly executed based upon the value of an item. The value can either be an integer type or an enum. We propose that a similar syntax be added for switching over types. We will refer to this as typeswitch. Object item; ... typeswitch (x : item) { case String: x.subString(2, 5); // use x as String case Number: x.intValue(); // use x as Number ... } The new typeswitch expression is of the form: typeswitch ( identifier : expression ) typeswitch_case_list where each typeswitch_case is of the form: case type_name : statement The above example typeswitch expands to this code: Object item; ... if(item instanceof String) { String x = (String) item; x.subString(2, 5); } else if(item instanceof Number) { Number x = (Number) item; } else ... As every typeswitch expression can be directly converted into existing Java expressions, we are not adding anything to the semantics of the language. However, we are arguably producing a much cleaner syntax that removes the need to write repetative and error-prone code. The colon seperating the identifier and the expression in the typeswitch has been chosen to mirror the use of the colon in the extended for loop. It would be possible to add a new keyword 'typeswitch' to Java, but this may cause problems. However, the syntax of typeswitch as presented here is easy to distinguish from that of switch, so the switch keyword could be re-used. To avoid type-safety issues, it would be necessary to prevent typeswitch cases from falling through, as integer and enum switches currently do. If fall-through was supported, then the above example would eroneously attempt to treat a String object as a Number. There is no need for a default clause, as Object can always be added as one of the cases. It would probably be good coding style to habitually supply a case that matches Object that raises an exception indicating that there is an unhandled type case. Cases are processed from top to bottom. It is an error to have a case where the type is incompatible with the type of the expression. For example, if the expression has type String, it would be an error to use any concrete class other than String and Object. However, it would be legal to use any interface that String does or may implement. It is an error for a case to be unreachable because a previous case will always accept the item. This should cover a lot of user errors. Summing up ---------- The addition of a typeswitch construct to Java could be done with minimal impact on existing code. It would address one of the last remaining legitimate causes of explicit casts not addressed by generics. By using the switch keyword with the new syntax, there would be no reason to expand the set of Java keywords. There are the arguments agains typeswitches that I can forsee. The semantics of a typeswitch are not the same as that for existing switch statements (in particular, the lack of fall-through on cases). This would tend to be an argument towards using something modelled on a type-aware if/else construct, such as: if(Type identifier : expression) body However, in the earlier discussions, this was ruled out. Adding typeswitch may encourage developers to use switch-over-type where polymorphism is the cleaner solution. However, some situations just can't be handled in this manner. The visitor design pattern is a work-arround for some of these problem cases. A typeswitch would allow visitor-like behavior to be implemented over class hierachies that don't have visitor support (for example, the java reflective Type hierachy). ###@###.### 2004-12-06 22:00:41 GMT