290 likes | 376 Vues
Explore Java programming concepts including polymorphism, generics, and Abstract Syntax Trees (AST). Learn to differentiate method invocations, control List objects with generics, and navigate ASTs for code analysis. Dive into Java syntax trees to grasp source code reconstruction and program transformation. Enhance your knowledge with practical examples and assignments to deepen your understanding.
E N D
SENG 531: Labs TA: Brad Cossette brad.cossette@gmail.com cossette@cpsc.ucalgary.ca http://pages.cpsc.ucalgary.ca/~cossette/ Office Hours: Monday, Wednesday 3-4pm ICT 524
Labs This Week: • Monday • Assignments Overview • Polymorphism in OO • Polymorphic Overloading • Wednesday • AST’s • Single vs. Double Dispatch • The Visitor Pattern
Assignment 1 Questions • You need to track each method invocation separately • e.g. don’t lump all moveBy() method invocations together • You should figure out the type each method is invoked on as a means of distinguishing between potentially similar method names
Assignment 1 Questions • Your output should be as informative as reasonably possible: • I should know which exactly which invocation/declaration you’re referring to at each point • The info available in a single node is very limited • Explore what data is available to you . . . • There are ways to augment this info . . .
Generics • Back in ye olden Java 1.4 days . . . public void oldPubCrawl( List pubs ) { for ( Iterator iii = pubs.iterator(); iii.hasNext(); ) { Bar current_bar = ( Bar )iii.next(); current_bar.drink(); current_bar.singVeryBadKaraoke(); } }
Generics • We have to cast, because List will take any object • Making List only take Bar objects means writing a specialized subclass public void oldPubCrawl( List pubs ) { for ( Iterator iii = pubs.iterator(); iii.hasNext(); ) { Bar current_bar = ( Bar )iii.next();
Generics Generics let us control what’s actually in a list… public void newestPubCrawl( List<Bar> pubs ) { for ( Iterator<Bar> iii = pubs.iterator(); iii.hasNext(); ) { Bar current_bar = iii.next(); current_bar.drink(); current_bar.singVeryBadKaraoke(); } } …without having separate customized classes.
Generics A better way to write this: public void newestPubCrawl( List<Bar> pubs ) { for ( Bar current_bar : pubs ) { current_bar.drink(); current_bar.singVeryBadKaraoke(); } }
Abstract Syntax Trees (AST) • Some compiler/language theory stuff: • Remember EBNF* form of context-free grammars? • When parsing a grammar, the rules when applied to some inputs can create a tree • If we ignore some syntactic symbols e.g. { }; etc. we can create an abstracted syntax tree . . . *Backus–Naur Form
Abstract Syntax Trees (AST) • Example: public class Foo { public int bar = 0; } public class FooBar { public static void main(String args[]) { Foo fighter = new Foo(); fighter.bar = 6; System.out.print( fighter.bar ); } } What could an AST for the outlined section look like?
Abstract Syntax Trees (AST) basic block statement statement = statement declaration create object statement field access Foo = fighter Foo System method invocation field access 6 out print fighter bar argument field access fighter bar
Abstract Syntax Trees (AST) • Key Points • You can recreate the source code from an AST • There’s not really an official standard for what an AST should look like This AST is different then the AST that Decaff will put out
AST: Why do you even care • Well, it’s part of the assignment
AST: Why do you even care • Well, it’s part of the assignment • Which is easier to search in? Why? • Source Code vs. AST? • Specifically, what is easier to find in an AST? • Program transformation (not in this course)
Suppose we have a tree of some sort: CPSC 331 – basic tree representation The Segueway: AST + Single Dispatch TreeNode argument TreeNode field access TreeNode TreeNode fighter bar
<<interface>> ITreeNode AbstractArgument ObjectReference FieldReference FieldAccess The Segueway: AST + Single Dispatch • Subclass TreeNode accordingly has Question: ITreeNode should probably be an Abstract class, not an Interface. Why?
AST Problem: Tree Traversal • Basic algorithm is easy • Example AST uses an in-order traversal • Others may use post-order • BUT! At each node, you need: • Code to determine what the node is • Code to handle that node’s children
AST Problem: Tree Traversal Why won’t this work? public abstract class AbstractTreeNode { protected AbstractTreeNode parent = null; protected List<AbstractTreeNode> children = null; protected String data = ""; }
AST Problem: Tree Traversal public class TreeTraversal { private AbstractTreeNode root_node = null; public TreeTraversal( AbstractTreeNode root ) { this.root_node = root; } public void traverse() { traverse( this.root_node ); } //Continued on Next Slide }
AST Problem: Tree Traversal public class TreeTraversal { //Continued protected void traverse( AbstractTreeNode node ) { if ( null != node.children && node.children.size() > 0 ) traverse( node.children.get( 0 ) ); System.out.println( "AbstractTreeNode: " + node.data ); if ( null != node.children && node.children.size() > 1 ) traverse( node.children.get( 1 ) ); } protected void traverse( Argument node ) { } protected void traverse( FieldAccess node ) { } protected void traverse( FieldReference node ) { } protected void traverse( ObjectReference node ) { } }
AST Problem: Tree Traversal public static void main( String args[] ) { Argument root = new Argument( "" ); FieldAccess fa = new FieldAccess( "." ); root.children.add( fa ); ObjectReference or = new ObjectReference( "fighter" ); FieldReference fr = new FieldReference( "bar" ); fa.children.add( or ); fa.children.add( fr ); TreeTraversal printer = new TreeTraversal( root ); printer.traverse(); } Output: AbstractTreeNode: fighter AbstractTreeNode: . AbstractTreeNode: bar AbstractTreeNode:
Tree Traversal + Single Dispatch • So we have two problems: • We need a common supertype to support a tree that we can traverse without custom code • We still need to have specific subclasses at each node to reflect different data • Java is single dispatch • How do we work around this?
Double Dispatch • Single Dispatch • The type of an object is calculated at compile time • Double Dispatch • The object’s type is determined at run-time • This is NOT duck-typing! • What’s the difference?
Double Dispatch • How do you simulated Double Dispatch? • Reflection • object instanceof X • Object.getClass() • Are there problems with this? • The Visitor design pattern • Simulate double dispatch by splitting responsibility
The Visitor Design Pattern • Simulates Double Dispatch: • The Visitor class expects an interface that it can call to transfer control • The called class is responsible for calling the appropriate method on the Visitor
The Visitor Design Pattern • The Visitor’s Target public interface class IVisitable { public void accept( AbstractVisitor visitor ); } • The Visitor “gives itself” to the class it visits • This still lets us take advantage of the common superclass
The Visitor Design Pattern • How to use the Visitor public class FieldAccess extends Argument implements IVisitable { //snip public void accept( AbstractVisitor visitor ) { left_child.accept( visitor ); visitor.visit( this ); //this = FieldAccess type object right_child.accept( visitor ); } } • Note that the call-back to the visitor now explicitly refers to the subclass type
The Visitor Design Pattern • Writing the Visitor public abstract class AbstractVisitor { //snip public void visit( FieldAccess object ) { //do stuff } } • Now we can take advantage of polymorphic overloading, without Java’s single dispatch model being a problem.
Labs Next Week: • Monday • Help with Assignment #1 • Wednesday • Help with Assignment #1