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 fromObject
- However, using
Object
creates the following problems:- Primitive types must be dynamically converted to their respective boxed types (i.e.
double
tojava.lang.Double
, etc.) - There are too many casts, leaving much room for error and reducing readability
- Elements of the list must be dynamically cast to desired types (i.e.
Double
) - There is no static assurance for type uniformity across members of the list
- 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
- Primitive types must be dynamically converted to their respective boxed types (i.e.
- In approach 1, we can use
- 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 typeList
: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 ofString
objects can specifyList<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
- 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
- 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
- The compiler instantiates the template, replacing all instances of
- Class templates must also be defined in the header file
- Class templates must be declared in the header file
- Template Specialization
- Specialized versions of class templates can be created for specific types
- Syntax:
template<> 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++