Type Safety and Enumerations
Type Checking and Type Errors The type system defines data types and the operations on data types. A type error occurs if an operation is performed on a value where the operation isn't defined or not well- defined. char c; scanf("%f", &c); /* error: double => char */ The translator (compiler, interpreter) seeks to avoid type errors by type checking. Type checking can be done statically or dynamically. Languages like Java also catch type errors at run-time and raise ("throw") exceptions.
Run-time Type Checking Student s = new Student("joe"); Object o = s.clone(); JFrame t = (JFrame) o; throws a ClassCastException
Type Safety (1) A program is considered "type safe" if it can be verified that it is free of type errors. In strongly typed languages like Java, this would seem like an easy task... but its not! Is the java.util.ArrayList class type safe?
Type Safety (2) Is the java.util.ArrayList class type safe? java.util.Arrays static void sort( Object [ ] array ); import java.awt.Color;... Object [ ] a = { new Double(0.5), Color.RED, "hello" }; java.util.Arrays.sort( a ); // run-time ClassCastException ! from the Java 2 SDK API
Type Safety (2) Arrays.sort throws a ClassCastException. Programmer isn't required to catch RuntimeExceptions, so this will compile OK but fail at run-time. Another example: Is the java.util.Date class type safe? java.util.Date public Date(int year, int month, int day); // Date(year-1900, month /* jan=0, feb=1 */, dom ) int year = 2000; int month = 0; // jan = 0, feb = 1, mar = 2,... int day = 1; // day of month Date newyear = new Date( year , month, day ); from the Java 2 SDK API
Type Safety (3) But, its sooo easy to forget... Date( year, month, day ) ??? Date( day, month, year ) ??? Date( month, day, year ) ??? int year = , month = 0, day = 1; Date d1 = new Date( year, month, day ); Date d2 = new Date( day, month, year ); Date d3 = new Date( month, day, year ); d1.toString() --> "Sat Jan 01 00:00:00 ICT 2000" d2.toString() --> "Wed Apr 10 00:00:00 ICT 1901" d3.toString() --> "Fri May 11 00:00:00 ICT 1900" more coffee, please
Type Safety (4) Calendar class has static constants for the months, but the values are same as "int". Calendar.JANUARY = 1 Calendar.FEBRUARY = 2 They doesn't help type checking or type safety. int year = , month = 0, day = 1; Date d1 = new Date( year, Calendar.JANUARY, day ); Date d2 = new Date( day, Calendar.JANUARY, year ); Date d3 = new Date( Calendar.JANUARY, day, year ); Its your fault!!!! You should have been more careful.
Enumerations: promoting type safety Enumerated types were introduced in Pascal. type months = ( jan, feb, mar, apr,..., dec ); procedure date(Year: int; Month: months; Day: int) begin... end; (* main method *) var thismonth : months; (* variable of months type *) begin thismonth := jan; date(2000, thismonth, 1); compiler can verify argument is valid. No brainer!
Java Enumerations: enum "enum" is like a class when static instances of itself. public enum Month { /* the allowed values of this type: */ JANUARY, FEBRUARY, MARCH, APRIL,... DECEMBER; } public class MyDate { int year = 2005; Month month = Month.DECEMBER; int day = 9; The values of this enumeration
enum can have attributes, too set the attributes in a constructor: public enum Month { /* the allowed values of this type: */ JANUARY("January",0), FEBRUARY("February",1), MARCH("March",2),... DECEMBER; /* attributes */ public final String name; private int index; /* the constructor */ Month( String name, String index) { this.name = name; this.index = index; }
enum members are printable If an enum has a toString( ) method, it is used. If an enum doesn't have toString( ), the enum member name is printed! System.out.println( Month.JANUARY ); System.out.println( Month.JANUARY.name ); Output: JANUARY January
A Safer Date Class public class MyDate { /* attributes */ private int year; private Month month; private int day; /* the constructor */ public MyDate( int year, Month month, int day) { this.year = year; this.month = month; this.day = day; } public String toString() { return ""+day+" "+month.name+" "+year; }
MyDate MyDate requires that the 2nd argument be a month: programmer can put in an invalid value. MyDate my = new Date(2005, Month.MAY, 1); Use the ".name" property to print month as string. MyDate d1 = new MyDate( 2005, Month.DECEMBER, 9 ); MyDate d2 = new MyDate( 2005, 12, 9 ); // Error System.out.println( "Today is " + d1); Today is 9 December 2005
enum values() method Java automatically supplies a method named values () that returns an array of all values in the enumeration. For the Month enum : Month [ ] mlist = Month.values( ); for( Month m : Month.values() ) // loop for all values System.out.println( m ); JANUARY FEBRUARY MARCH APRIL MAY JUNE...
enum for Token Types public enum TokenType { /* the allowed values of this type: */ IDENT( "[A-Za-z_]\\w*" ), NUMBER( "(\\d+\\.?|\\.\\d)\\d*([Ee][+-]?\\d+)?" ), OPERATOR( "[-+*/^]" ), ASSIGN( "=" ), SEPARATOR( "[()]" ); /* attributes */ public final String regex; public final Pattern pattern; /* constructor */ Tokens( String regex ) { this.regex = regex; this.pattern = Pattern.compile(regex); } Good OO design: object contains its own properties
Using Token Types public class Token { /* token has a type and value */ private TokenType type; private String value; Token(TokenType type, Double value) { this.type = type; this.value = value; } /* return the token type */ TokenType type() { return type; } } enum members are unique. You can use '==' to test if ( token.type() == TokenType.IDENT )... if ( token.type() == TokenType.NUMBER )...
Advantages of using Enumeration 1. Type safety. 2. Collect all properties of a token in a single place (token name, token regular expression,...) 3. More efficient than using String for testing. 4. Avoid mistyping of String name in comparison. Compare: if ( token.type.equals("NUMBER") )... if ( token.type.equals("IDENT") )... If you use the wrong string value then the error is not detected, but the code won't work. if ( token.type == Token.NUMBER )... if ( token.type == Token.IDENT )...
How to Hide the enum To minimize changes in your program, you can try... public class Token { public static TokenType IDENT = TokenType.IDENT; public static TokenType NUMBER = TokenType.NUMBER;... /* can make "type" attribute immutable public */ public final TokenType type;... } if ( token.type == Token.IDENT )... if ( token type == Token.NUMBER )... same as TokenType.NUMBER
Compare all token types In your tokenizer class, you can use values(): public class Tokenizer { String s; TokenType type = null;... /* can make "type" attribute immutable public */ for ( TokenType t : TokenType.values() ) if ( s.matches( t.regex ) ) type = t; token = new Token( s, type );