Object-Oriented Programming


NYU, Undergraduate Division, Computer Science Course - Fall 2013

OOP Class Notes for 10/10/13

Java Generics

  • If we wanted to implement two singly linked lists, one of doubles and one of strings. What should we do?
    • In approach 1, we can use Object to represent the members of each list because all Java classes inherit from Object
    • However, using Object creates the following problems:
      1. Primitive types must be dynamically converted to their respective boxed types (i.e. double to java.lang.Double, etc.)
      2. There are too many casts, leaving much room for error and reducing readability
      3. Elements of the list must be dynamically cast to desired types (i.e. Double)
      4. There is no static assurance for type uniformity across members of the list
      5. There is no static assurance that a method acting on lists of a given type is actually called on a list of that type; we only know that said method acts on a list of some type
  • We can improve this code by implementing the list using Java generics
  • Java Generics
    • Add type parameters to classes
    • Syntax for defining type parameter T of generic type List:
      public class List<T> {
    • To create lists, a concretely defined type argument must be supplied to the constructor (e.g. new List<String>( /* */ ))
    • Methods acting on lists of a specific type can now specify List<T> as a parameter. For example, a method that works on a list of String objects can specify List<String> as a parameter
    • Now, you have static safety in your lists; the compiler will detect errors generated when an element of a list does not match the predefined type of the list
  • How are generics implemented by the Java compiler?
    • Type erasure -- removes generic type information from source code, adds casts as needed, and produces byte code
      • Essentially, the <s and >s go away and the compiler generates exactly the same naive code we wrote in approach 1
    • Why is this the way the compiler works?
      • Because it does not require the JVM to be changed (generics were only introduced with version 1.5 of the Java language)
      • Type erasure still allows the compiler to catch static type safety errors
  • Issues with Java Generics:
    • No generic arrays or instantiations using the new keyword
    • Can't handle primitive types, which require wrapper classes
      • Java does have auto-boxing and auto-unboxing to make conversions from primitive types to boxed types and back again, but this has an overhead cost

C++ Templates

  • Templates are the C++ version of Java Generics, except that they are more flexible
    • Templates can be made for classes and functions, and said templates allow classes and functions to intake many different types
    • You can implement new keyword instantiations, generic arrays and have static fields that are generic
  • Class Templates
    • Class parameters can be generically defined to increase flexibility of a class
    • Syntax:
      template<typename T /* further generic parameters */ >
      class List {
    • Using this approach, data structures can be created and used to handle many different types without declaring separate classes for each
  • Declaring and Defining Templates
    • Class templates must be declared in the header file
      • The compiler instantiates the template, replacing all instances of T with the actual type
    • Class templates must also be defined in the header file
  • Template Specialization
    • Specialized versions of class templates can be created for specific types
    • Syntax:
      class Name<Types> {
    • For the specified types, the compiler will use the specialized templates
    • Defined in .cc (implementation) files because they don't need to be instantiated by the compiler
    • For array templates in our project (the only time we can use templates), we cannot define the __class() method in the header file because the dynamic type of the returned class changes depending on the given array type
  • Templates allow us, at a high level, to write code that abstracts over a type so we can write the code only once, giving us type safety in C++