200 likes | 244 Views
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.
E N D
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 ); from the Java 2 SDK API import java.awt.Color; ... Object [ ] a = { new Double(0.5), Color.RED, "hello" }; java.util.Arrays.sort( a ); // run-time ClassCastException !
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); from the Java 2 SDK API // 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 - 1900, month, day );
Type Safety (3) • But, its sooo easy to forget... • Date( year, month, day ) ??? • Date( day, month, year ) ??? • Date( month, day, year ) ??? more coffee, please int year = 2000 - 1900, 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 ); OK! 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"
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 = 2000 - 1900, 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 ); OK! 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); No brainer! compiler can verify argument is valid.
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; } The values of this enumeration public class MyDate { int year = 2005; Month month = Month.DECEMBER; int day = 9;
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 • Good OO design: object contains its own properties 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); }
Using Token Types • enum members are unique. You can use '==' to test if ( token.type() == TokenType.IDENT ) ... if ( token.type() == TokenType.NUMBER ) ... 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; } }
Advantages of using Enumeration • Type safety. • Collect all properties of a token in a single place (token name, token regular expression, ...) • More efficient than using String for testing. • 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 asTokenType.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 );