QUESTIONS LAST TIME: - Ontime quiz, SiteRiter questions, more grammars and parsing TODAY: - SiteRiter questions - More top-down parsing - Enum subtleties - Quiz return PROJECT 1 STATUS DAY 13: +--------------+ - Interim deliverable #2 Wed Sep 11, 2013 12OON MST |UNDER ONE DAY!| - Interim deliverable #1 ALL DONE +--------------+ DEFINITION OF THE DAY TECHNICAL DEBT, n. During the planning or execution of a software project, what you incur when you defer necessary work. Like ordinary financial debt, technical debt incurs interest, in the form of extra work downstream, which can eventually cripple or bankrupt a project entirely if it is not paid off by cleaning up the code or design. PROJECT 1 QUESTIONS - Is the example in (C.8.3.2) broken? - What's a token? What's the difference between a token and a char? PROJECT 1 TIMELINE ((T.2.4)) When: Sat Sep 7, 2013 12NOON MST What: SiteRiter reference implementation made accessible online. +-----------------------------------------------------------------------+ |((T.2.5)) INTERIM DELIVERABLE#2 DEADLINE [START+14 DAYS]| |When: Wed Sep 11, 2013 12OON MST | |What: Last possible ON-TIME code turn-in of: | | (1) Updated & extended JUnit tests for SDLParser | | (2) A compilable, runnable, clean and feature-complete | | implementation of SDLParserImpl, as | | com.putable.siteriter.YOURNAME.SDLParserImpl, that fully and | | correctly implements com.putable.siteriter.SDLParser. | | All spec-required time and space performance bounds must be | | met by your implementation, but your JUnit tests only have | | to test function, not space/time performance. | +-----------------------------------------------------------------------+ ((T.2.6)) When: Sat Sep 14, 2013 12NOON MST What: SDLParser JUnit tests released GRAMMARS AND PARSING - A grammar is a way of describing a set of strings in a language - A grammar consists of a set of RULES describing how legal inputs can be put together, and a START SYMBOL saying where to begin - Grammar rules consist of TERMINAL symbols (like "drink" and "slurm") and NON-TERMINAL symbols (like FOO and BAR) - Terminal symbols appear only as leaves in legal inputs; non-terminal symbols only as interior nodes in legal inputs - Grammar rules can use '|' to express 'either or'. FOO := ( a + b )|c - Grammar rules can use '+' to represent 'and then'. - Grammar rules can be recursive - Lots of different ways that grammars are written. Just figure out what counts as terminal, what counts as non-terminal, what counts as sequence and alternation, and what counts as the start symbol, and then the surface notation doesn't matter TOP-DOWN PARSING - LOOKAHEAD FOO <- BAR | BLETCH BAR <- "a" FOO BLETCH <- "b" "a" import java.io.*; class Test { static int peek(Reader in) throws IOException { in.mark(1); // Nice.. Can we count on this? int ch = in.read(); in.reset(); return ch; } static boolean parseFoo(Reader in) throws IOException { int ch = peek(in); if (ch=='a') return parseBar(in); if (ch=='b') return parseBletch(in); } static boolean parseBar(Reader in) throws IOException { ?? } static boolean parseBletch(Reader in) throws IOException { ?? } public static void main(String[] arg) throws IOException { .. TOP-DOWN PARSING - BUILDING A PARSE TREE - If you just need to know if something's grammatical, or do only do simple things, you can often just build a recognizer. If you need to DO something based on the input, you often want to build a parse tree. - Can make a (static) method for each rule in the grammar, that returns a pointer to (the root of) a parse tree - Each method can peek ahead into the istream to decide what to do. It might read terminal symbols itself and/or call other rules to read non-terminal symbols and/or die on an error. - If it succeeds it builds a parse tree node (perhaps containing other legal inputs obtained by reading other non-terminals) and returns it. - Depending on the uses of the resulting data structures, they may or less closely-resemble the grammar that describes the language. So long as the language semantics are strictly preserved, that's fine. INTERLUDE FROM THE GEEK BAR What does this program print? class Test { public static void main(String[] args) { System.out.print("THE QUESTION IS: "); /************************************************* * Heh heh, we just process their text via URL * * interpolation, but pretend we did it ourselfs!* *************************************************/ http://google.com/q= + System.in.read(); System.out.println("ARE YOU AMAZED?"); } } INTERLUDE FROM THE GEEK BAR What does this program print? class Test { public static void main(String[] args) { System.out.print("THE QUESTION IS: "); /************************************************* * Heh heh, we just process their text via URL * * interpolation, but pretend we did it ourselfs!* *************************************************/ http://google.com/q= + System.in.read(); System.out.println("ARE YOU AMAZED?"); } } Three views: Lexical, syntactic, semantic TOP-DOWN PARSING - BUILDING A PARSE TREE Start symbol == S S <- "[" + FILL + "]" | "<" + FILL + ">" FILL <- "" | "a" + FILL | "b" + FILL class S { class S { char left; boolean isSquare; //?? S FILL middle; Fill body; /|\ char right; } / | \ } class Fill { "<" | ">" class FILL { char first; //unless rest==null | String first; //?? Fill rest; FILL FILL rest; } / \ } "b" FILL / \ "a" FILL | "" TOP-DOWN PARSING - FALLING FARTHER FROM THE TREE Start symbol == S // S <- "[" + FILL + "]" | "<" + FILL + ">" // FILL <- "" | "a" + FILL | "b" + FILL // S // /|\ - Sometimes the grammar is mostly important to tell you // / | \ what's legal, and (some details of) the shape of the // "<" | ">" parse tree are less important. // | // FILL // | // "ba" // ///////////////// TOP-DOWN PARSING - FALLING FARTHER FROM THE TREE Start symbol == S // S <- "[" + FILL + "]" | "<" + FILL + ">" // FILL <- "" | "a" + FILL | "b" + FILL // S // /|\ - Sometimes the grammar is mostly important to tell you // / | \ what's legal, and (some details of) the shape of the // "<" | ">" parse tree are less important. // | // FILL import java.io.*; // | class Fill { // "ba" private StringBuffer body = new StringBuffer(); // public static Fill parse(Reader in) throws IOException { ///////////////// Fill f = new Fill(); while (true) { in.mark(1); int ch = in.read(); if (ch=='a' || ch=='b') f.body.append((char)ch); else break; } in.reset(); return f; } public String toString() { return "F:"+body; } } TOP-DOWN PARSING - BUILDING A PARSE TREE - If you just need to know if something's grammatical, or do only do simple things, you can often just build a recognizer. If you need to DO something based on the input, you often want to build a parse tree. - Can make a (static) method for each rule in the grammar, that returns a pointer to (the root of) a parse tree - Each method can peek ahead into the istream to decide what to do. It might read terminal symbols itself and/or call other rules to read non-terminal symbols and/or die on an error. - If it succeeds it builds a parse tree node (perhaps containing other legal inputs obtained by reading other non-terminals) and returns it. - Depending on the uses of the resulting data structures, they may or less closely-resemble the grammar that describes the language. So long as the language semantics are strictly preserved, that's fine. TOP-DOWN PARSING - BUILDING A PARSE TREE - If you just need to know if something's grammatical, or do only do simple things, you can often just build a recognizer. If you need to DO something based on the input, you often want to build a parse tree. - Can make a (static) method for each rule in the grammar, that returns a pointer to (the root of) a parse tree - Each method can peek ahead into the istream to decide what to do. It might read terminal symbols itself and/or call other rules to read non-terminal symbols and/or die on an error. - If it succeeds it builds a parse tree node (perhaps containing other legal inputs obtained by reading other non-terminals) and returns it. - Depending on the uses of the resulting data structures, they may or less closely-resemble the grammar that describes the language. So long as the language semantics are strictly preserved, that's fine. - Can we use top-down parsing for all possible grammars? TOP-DOWN PARSING - Sometimes top-down parsing is easy to code TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO | n TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO | n yes TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO | /|\ n n a n yes TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO | /|\ n n a n yes no TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO FOO | /|\ /|\ n n a n n a FOO \ yes no n TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO FOO | /|\ /|\ n n a n n a FOO \ yes no yes n TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO FOO | /|\ /|\ n n a n n a FOO \ yes no yes n static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() != 'n') { r.reset(); return false; } TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO FOO | /|\ /|\ n n a n n a FOO \ yes no yes n static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() != 'n') { r.reset(); return false; } r.mark(1); if (r.read() == 'a') { return recognize(r); } TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO FOO | /|\ /|\ n n a n n a FOO \ yes no yes n static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() != 'n') { r.reset(); return false; } r.mark(1); if (r.read() == 'a') { return recognize(r); } r.reset(); return true; } TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO FOO | /|\ /|\ n n a n n a FOO \ yes no yes n import java.io.*; class Test { static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() != 'n') { r.reset(); return false; } r.mark(1); if (r.read() == 'a') { return recognize(r); } r.reset(); return true; } public static void main(String[] args) { Reader r = new StringReader("nnna"); try { for (int i = 0; i<3; ++i) System.out.print(" "+recognize(r)); } catch (IOException e) { System.out.println("Died:"+e); } } } * TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO FOO | /|\ /|\ n n a n n a FOO \ yes no yes n import java.io.*; class Test { static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() != 'n') { r.reset(); return false; } r.mark(1); if (r.read() == 'a') { return recognize(r); } r.reset(); return true; } public static void main(String[] args) { Reader r = new StringReader("nnna"); try { for (int i = 0; i<3; ++i) System.out.print(" "+recognize(r)); } catch (IOException e) { System.out.println("Died:"+e); } } } true true false TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "n" | "n" + "a" + FOO Start symbol == FOO FOO FOO FOO | /|\ /|\ n n a n n a FOO \ yes no yes n import java.io.*; class Test { static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() != 'n') { r.reset(); return false; } r.mark(1); if (r.read() == 'a') { return recognize(r); } r.reset(); return true; } public static void main(String[] args) { Reader r = new StringReader("nnna"); try { for (int i = 0; i<3; ++i) System.out.print(" "+recognize(r)); } catch (IOException e) { System.out.println("Died:"+e); } } } Hey! Why isn't that 'true true true', with an 'a' true true false left over on the input?? There ARE three FOO's there! TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "a" | FOO + "z" Start symbol == FOO TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "a" | FOO + "z" Start symbol == FOO FOO FOO FOO | / \ / \ a a z FOO z | a TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO <- "a" | FOO + "z" Start symbol == FOO FOO FOO FOO | / \ / \ a a z FOO z | yes no a yes TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO FOO <- "a" | FOO + "z" / \ Start symbol == FOO FOO z / \ FOO FOO FOO FOO z | / \ / \ / \ a a z FOO z FOO z | | yes no a yes a yes TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO FOO <- "a" | FOO + "z" / \ Start symbol == FOO FOO z / \ FOO FOO FOO FOO z | / \ / \ / \ a a z FOO z FOO z | | yes no a yes a yes static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() == 'a') return true; if (!recognize(r)) return false; r.mark(1); if (r.read() == 'z') return true; r.reset(); return false; } TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO FOO <- "a" | FOO + "z" / \ Start symbol == FOO FOO z / \ FOO FOO FOO FOO z | / \ / \ / \ a a z FOO z FOO z | | yes no a yes a yes class Test { static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() == 'a') return true; if (!recognize(r)) return false; r.mark(1); if (r.read() == 'z') return true; r.reset(); return false; } .. Reader r = new StringReader("aaz"); .. } $ java Test TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO FOO <- "a" | FOO + "z" / \ Start symbol == FOO FOO z / \ FOO FOO FOO FOO z | / \ / \ / \ a a z FOO z FOO z | | yes no a yes a yes class Test { static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() == 'a') return true; if (!recognize(r)) return false; r.mark(1); if (r.read() == 'z') return true; r.reset(); return false; } .. Reader r = new StringReader("aaz"); .. } $ java Test true trueException in thread "main" java.lang.StackOverflowError $ TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy FOO FOO <- "a" | FOO + "z" / \ Start symbol == FOO FOO z / \ FOO FOO FOO FOO z | / \ / \ / \ a a z FOO z FOO z | | yes no a yes a yes class Test { static boolean recognize(Reader r) throws IOException { r.mark(1); if (r.read() == 'a') return true; if (!recognize(r)) return false; //<<<<<<<'infinite' recursion.. r.mark(1); if (r.read() == 'z') return true; r.reset(); return false; } .. Reader r = new StringReader("aaz"); .. } $ java Test true trueException in thread "main" java.lang.StackOverflowError $ TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy - Grammars requiring a lot of 'lookahead' are a problem e.g, FOO <- "n" | "n" + "a" + FOO TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy - Grammars requiring a lot of 'lookahead' are a problem e.g, FOO <- "n" | "n" + "a" + FOO - Grammars containing 'left recursion' are a problem e.g, FOO <- "a" | FOO + "z" TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy - Grammars requiring a lot of 'lookahead' are a problem e.g, FOO <- "n" | "n" + "a" + FOO - Grammars containing 'left recursion' are a problem e.g, FOO <- "a" | FOO + "z" - Sometimes there are tricks you can do to REWRITE grammar rules so that they describe the same legal inputs but are easier to parse. You end up with DIFFERENT legal inputs, but sometimes you don't care. TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy - Grammars requiring a lot of 'lookahead' are a problem e.g, FOO <- "n" | "n" + "a" + FOO - Grammars containing 'left recursion' are a problem e.g, FOO <- "a" | FOO + "z" - Sometimes there are tricks you can do to REWRITE grammar rules so that they describe the same legal inputs but are easier to parse. You end up with DIFFERENT legal inputs, but sometimes you don't care. FOO <- "a" | FOO + "z" TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy - Grammars requiring a lot of 'lookahead' are a problem e.g, FOO <- "n" | "n" + "a" + FOO - Grammars containing 'left recursion' are a problem e.g, FOO <- "a" | FOO + "z" - Sometimes there are tricks you can do to REWRITE grammar rules so that they describe the same legal inputs but are easier to parse. You end up with DIFFERENT legal inputs, but sometimes you don't care. We could rewrite FOO <- "a" | FOO + "z" as FOO <- "a" | "a" + ZEES ZEES <- "z" | "z" + ZEES TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy - Grammars requiring a lot of 'lookahead' are a problem e.g, FOO <- "n" | "n" + "a" + FOO - Grammars containing 'left recursion' are a problem e.g, FOO <- "a" | FOO + "z" - Sometimes there are tricks you can do to REWRITE grammar rules so that they describe the same legal inputs but are easier to parse. You end up with DIFFERENT legal inputs, but sometimes you don't care. We could rewrite FOO <- "a" | FOO + "z" as FOO <- "a" | "a" + ZEES ZEES <- "z" | "z" + ZEES FOO FOO FOO FOO | / \ / \ / \ a a z FOO z a ZEES | | yes no a no z (was yes) yes TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy - Grammars requiring a lot of 'lookahead' are a problem e.g, FOO <- "n" | "n" + "a" + FOO - Grammars containing 'left recursion' are a problem e.g, FOO <- "a" | FOO + "z" - Sometimes there are tricks you can do to REWRITE grammar rules so that they describe the same legal inputs but are easier to parse. You end up with DIFFERENT legal inputs, but sometimes you don't care. We could rewrite FOO <- "a" | FOO + "z" as FOO FOO <- "a" | "a" + ZEES / \ ZEES <- "z" | "z" + ZEES FOO z / \ FOO FOO FOO FOO FOO z | / \ / \ / \ / \ a a z FOO z a ZEES FOO z | | | yes no a z a no no yes TOP-DOWN PARSING - Sometimes top-down parsing is easy to code - Sometimes it's not so easy - Grammars requiring a lot of 'lookahead' are a problem e.g, FOO <- "n" | "n" + "a" + FOO - Grammars containing 'left recursion' are a problem e.g, FOO <- "a" | FOO + "z" - Sometimes there are tricks you can do to REWRITE grammar rules so that they describe the same legal inputs but are easier to parse. You end up with DIFFERENT legal inputs, but sometimes you don't care. We could rewrite FOO <- "a" | FOO + "z" as FOO FOO FOO <- "a" | "a" + ZEES / \ / \ ZEES <- "z" | "z" + ZEES a ZEES FOO z / \ / \ FOO FOO FOO FOO z ZEES FOO z | / \ / \ / \ / \ / \ a a z FOO z a ZEES z ZEES FOO z | | | | yes no a z z a no no yes yes TOP-DOWN PARSING - UPSHOTS - Recursive descent top-down parsing works well for certain sorts of grammars and not for others. - They're the easiest kind of parsers to write by hand. - Because of the restrictions on the grammars they can handle (e.g., no left recursion), other kinds of parsers are used more commonly. TOP-DOWN PARSING - UPSHOTS - Recursive descent top-down parsing works well for certain sorts of grammars and not for others. - They're the easiest kind of parsers to write by hand. - Because of the restrictions on the grammars they can handle (e.g., no left recursion), other kinds of parsers are sometimes used, at least traditionally. - The main group of which is called `bottom up' parsers. (See, e.g., CS454) ASPECTS OF JAVA MASTERY: ENUM enum TextOp { UPPER { public String fix(String s) { return s.toUpperCase(); } }, LOWER { public String fix(String s) { return s.toUpperCase(); } }, PIGLATIN { public String fix(String s) { return s.replaceFirst("^(.*?)([aeiouAEIOU].*)$","$2$1ay"); } }; public abstract String fix(String in); }; public class Test { public static void main(String[] args) { String s = "Java"; TextOp top = TextOp.PIGLATIN; System.out.println(top.fix(s)); } } * ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. enum Suit { SPADES, HEARTS, DIAMONDS, CLUBS; }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); } } ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; } public String getName() { return name; } private String name; }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; } public String getName() { return name; } private String name; }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } - Suppose we also wanted to be able to go the other way, from "picos" to Suit.SPADES? ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; } public String getName() { return name; } private String name; }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } - Suppose we also wanted to be able to go the other way, from "picos" to Suit.SPADES? - We could build a map to remember the associations.. Map foo = ..; foo.put("picos",Suit.SPADES); ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; } public String getName() { return name; } private String name; }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } - Suppose we also wanted to be able to go the other way, from "picos" to Suit.SPADES? - We could build a map to remember the associations.. Map foo = ..; foo.put("picos",Suit.SPADES); - Where should we put the map? A static map, say, in Suit.. ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. import java.util.*; enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; } public String getName() { return name; } private String name; private static Map names = new HashMap(); <<< names = new HashMap(); <<< names = new HashMap(); <<< names = new HashMap(); private static void add(String n, Suit s) { names.put(n,s); } }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } * ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. import java.util.*; enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; add(name,this); } public String getName() { return name; } private String name; private static Map names = new HashMap(); private static void add(String n, Suit s) { names.put(n,s); } }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } Exception in thread "main" java.lang.ExceptionInInitializerError at Test.main(Test.java:15) Caused by: java.lang.NullPointerException at Suit.add(Test.java:11) .. ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. import java.util.*; enum Suit { The enum 'constants' are really static instances! Like: SPADES("picos"), --->> public static final Suit SPADES = new Suit("picos"); HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; add(name,this); } public String getName() { return name; } private String name; private static Map names = new HashMap(); private static void add(String n, Suit s) { names.put(n,s); } }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } Exception in thread "main" java.lang.ExceptionInInitializerError at Test.main(Test.java:15) Caused by: java.lang.NullPointerException at Suit.add(Test.java:11) .. ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. import java.util.*; enum Suit { The enum 'constants' are really static instances! Like: SPADES("picos"), --->> public static final Suit SPADES = new Suit("picos"); HEARTS("corazones"), DIAMONDS("diamantes"), So they initialize when? And in what order? CLUBS("trébol"); Compared to the initialization Suit(String s) { name = s; add(name,this); } of this guy? ------------\ public String getName() { return name; } | private String name; | private static Map names = new HashMap(); <<--/ private static void add(String n, Suit s) { names.put(n,s); } }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } Exception in thread "main" java.lang.ExceptionInInitializerError at Test.main(Test.java:15) Caused by: java.lang.NullPointerException at Suit.add(Test.java:11) .. ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. import java.util.*; enum Suit { The enum 'constants' are really static instances! Like: SPADES("picos"), --->> public static final Suit SPADES = new Suit("picos"); HEARTS("corazones"), DIAMONDS("diamantes"), So they initialize when? And in what order? CLUBS("trébol"); Compared to the initialization Suit(String s) { name = s; add(name,this); } of this guy? ------------\ public String getName() { return name; } | private String name; | private static Map names = new HashMap(); <<--/ private static void add(String n, Suit s) { names.put(n,s); } }; Sigh. Need class Test { to do 'lazy public static void main(String[] args) { initializtion' System.out.println(Suit.CLUBS); of the Map.. System.out.println(Suit.CLUBS.getName()); } } Exception in thread "main" java.lang.ExceptionInInitializerError at Test.main(Test.java:15) Caused by: java.lang.NullPointerException at Suit.add(Test.java:11) .. ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. import java.util.*; enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; add(name,this); } eager initialization--\ public String getName() { return name; } | private String name; | private static Map names = new HashMap(); <<--/ private static void add(String n, Suit s) { names.put(n,s); } }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. import java.util.*; enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; add(name,this); } public String getName() { return name; } private String name; private static Map names; private static void add(String n, Suit s) { if (names == null) names = new HashMap(); <<-- 'lazy names.put(n,s); initialization' } }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); } } ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. import java.util.*; enum Suit { SPADES("picos"), HEARTS("corazones"), DIAMONDS("diamantes"), CLUBS("trébol"); Suit(String s) { name = s; add(name,this); } public String getName() { return name; } public static Suit fromName(String s) { return names.get(s); } private String name; private static Map names; private static void add(String n, Suit s) { if (names == null) names = new HashMap(); names.put(n,s); } }; class Test { public static void main(String[] args) { System.out.println(Suit.CLUBS); System.out.println(Suit.CLUBS.getName()); System.out.println(Suit.fromName("diamantes")); } } * ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. - Enum constant values are really public static final instances. - static fields in a class initialize in lexical order. - In 'regular classes' you can rearrange the lexical order to suit your needs. - In 'enum's, the (public static final) constant value instances always and necessarily come first. ASPECTS OF JAVA MASTERY: ENUM - BUT THERE'S ALWAYS GOTCHAS.. - Enum constant values are really public static final instances. - static fields in a class initialize in lexical order. - In 'regular classes' you can rearrange the lexical order to suit your needs. - In 'enum's, the (public static final) constant value instances always and necessarily come first. - Lazy initialization. QUIZ RETURN