Object-Oriented Programming

CSCI-UA.0470-001

NYU, Undergraduate Division, Computer Science Course - Fall 2013

OOP Class Notes for 11/05/13

Comments on OOP Midterm Projects (Some issues you may want to consider fixing...)


try{ if (myCppFiles!=null)myCppFiles.closeFiles();System.out.println("CLOSING FILE: "+myFileDep.getFilePath());}
catch(Exception e){System.out.println("closing failed");}
}
  • Catches the exception, prints an error message then continues execution. The exception is ignored, and probably causes problems down the line.
  • Do something meaningful with the error, don't just catch it and ignore it.
  • Use System.err for error output rather than System.out
  • You are not payed by the line, yet, but use line breaks and indentation to improve code readability. Adding line breaks would also reveal that the code after the if condition should be enclosed by { }.

GNode leftSide = // ...

if (leftSide.getName() == "SelectionExpression") {
  // ...
}
  • You can only use '==' on internal (intern) strings, so this is pretty brittle. Note that Java string literals are guaranteed to be intern-ed. We do not know about names in GNode (they happen to be provided by string literals as well).
  • Xtc already provides a method for this case. Use Node.hasName(String) to check the name of a node.

public void visitForStatement(GNode n) { 
  interior(n);
  visit(n);
}

public void visitExpressionStatement(GNode n) { 
  interior(n);
  visit(n);
}

public void visitForStatement(GNode n) { 
  interior(n);
  visit(n);
}

public void visitConditionalStatement(GNode n) { 
  interior(n);
  visit(n);
}

public void visitReturnStatement(GNode n) { 
  interior(n);
  visit(n);
}

// ... and then later

if (current.equals("ExpressionStatement")) {
  contents.add(new ExpressionStatement(n.getNode(i)));
}
else if (current.equals("ForStatement")) {
  contents.add(new ForStatement(n.getNode(i)));
}
else if (current.equals("WhileStatement")) {
  contents.add(new WhileStatement(n.getNode(i)));
}
else if (current.equals("FieldDeclaration")) {
  contents.add(new FieldDeclaration(n.getNode(i)));
}
else if (current.equals("ConditionalStatement")) {
  contents.add(new ConditionalStatement(n.getNode(i)));
}
else if (current.equals("ConstructorDeclaration")) {
  contents.add(new ConstructorDeclaration(n.getNode(i)));
}
else if (current.equals("MethodDeclaration")) {
  methods.add(new MethodDeclaration(n.getNode(i)));
}
else if (current.equals("ReturnStatement")) {
  contents.add(new ReturnStatement(n.getNode(i)));
}
  • A lot of repetitive code usually means there's a better way to do it.
  • In this case, use the visitor pattern as it is intended to be used.

GNode initializedDeclarator = GNode.create("InitializedDeclarator");
initializedDeclarator.add(0, null);
initializedDeclarator.add(1, GNode.create("SimpleDeclarator").add(className));
initializedDeclarator.add(2, null);
initializedDeclarator.add(3, null);
initializedDeclarator.add(4, null);
  • The index is completely unnecessary in this case (and a likely cause of future bugs), just call add(Object), which adds a child to the end.

cppFm.write("\n\t\ttemplate<>\n"+
            "\t\tClass __Array<"+
            myTree.getCppPakageName()+myTree.myClassnameString+">::__class() {\n"+
            "\t\t\tstatic Class k = new __Class(__rt::stringify(\"[L"+
            myInfo.getPackageName()+"."+myTree.myClassnameString+"\"),\n"+
            "\t\t\t\t\t\t\t\t\t__Array<"+myTree.mySuperClass.getCppPakageName()+
            myTree.mySuperClass.myClassnameString+">::__class(),\n"+
            "\t\t\t\t\t\t\t\t\t"+myTree.getCppPakageName()+"__"+myTree.myClassnameString+"::__class());\n"+
            "\t\t\treturn k;\n"+
            "\t\t}\n\n");
  • Hacking together a string like this is likely slow. Java's StringBuilder helps with incremental string creation and is more efficient. Do NOT use StringBuffer, which is synchronized.
  • Even better: Using xtc's Printer, once again, would be much easier and a better fit to the problem (formatting source code). Simply have the printer write to a StringWriter...

for (int i = 0; i < nodeSize; i++) {
  try {
    System.out.println("part " + i + ": " + n.getNode(i));
  }
  catch (ClassCastException e) {
    System.out.println("part " + i + ": " + n.getString(i));
  }
}
  • Exception handling is an expensive mechanism and should be avoided in regular code if possible.
  • In this case, use get(int) to retrieve the i-th child and then conditionally branch on its dynamic type. No need to use execeptions here.

Java Reflection

  • Java Reflection
    • Reflection is a language facility that lets you get information about and manipulate objects at runtime.
    • It is useful for writing programs that deal with objects in a general way, such as programmer tools.
      • JUnit and xtc's Visitor are two examples.
    • Compared to direct invocation, reflection has poor performance.
  • java.lang.reflect
    • Classes that describe the features of java types:
      • Class - describe a type (in java.lang)
      • Package - describe a package
      • Field - describe a field, allow inspection and modification
      • Method - describe a method, allow invocation
      • Constructor - describe a constructor, allow its invocation
      • Array - methods to analyze arrays
    • You can use the class loader to get an instance of a Class:
      import java.lang.reflect.*;
      Class c = Class.forName("ClassName");
    • Where "ClassName" is the name of any class, with its package name (for example: java.lang.String).
    • Some of the methods that the Class class contains are:
      getSuperclass()
      getInterfaces()
      getPackage()
      getDeclaredFields()
      getDeclaredMethods()
      getDeclaredConstructors()
    • A more complete listing is found here
  • The Method class
    • Method describes a method but isn't a method itself.
    • Method's invoke function can be used to invoke the method described by an instance of Method.
    • The arguments to the invoke function is an object on which to invoke the method, and the arguments to the method
    • myMethod.invoke(myObj, args);
    • Reflection allows you to modify access-level modifiers (for example: private,protected).
      myMethod.setAccessible(true);
    • This can be abused, so we have java.lang.SecurityManager to block such things

Visitor Dispatch

  • Contract of method dispatch
    • Determine the closest matching visit method
    • Invoke it on the visited node
    • Return the result
    • If the node is null, return null
    • Note that xtc's caching is not threadsafe
  • Stepping through dispatch
    • Uses a linked hashmap to cache methods already found.
      • The CacheKey uses the hashCode and equals methods of the visitor and the node.
    • First check the cache for the method.
    • If not found in the cache, get the method with findMethod(), and cache the result.
    • Invoke the method on "this", with the GNode as the parameter.
    • Return the result
  • Stepping through findMethod
    • Get an instance of the Class object for the visitor
    • If the visited node n is a generic node, then
      • use reflection to see if that Class object has a method named "visit" + n.getName()
      • otherwise check whether it has method named visit that takes a GNode or a Node.
      • The type parameter distinguishes between overloaded methods
    • If the visited node n is not a generic node, then
      • look whether the Class object for the visitor has a method named visit that takes an object of n's class.
      • If it doesn't, then get the interfaces that the class of n implements, and look for the visit methods accepting objects of these interface types
      • If the methods are still not found, repeat the search on the superclass of n's class
    • Throw an exception if no visit method has been found.
    • Otherwise, return the method.