PATTERNS09 - Generics in .NET and Java


Published on

Generics in .NET and Java. Suitable for intermediate to advanced computing students and those studying software engineering.

Published in: Software, Technology
  • Be the first to comment

  • Be the first to like this

No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

PATTERNS09 - Generics in .NET and Java

  1. 1. Generics Michael Heron
  2. 2. Introduction  There is a common technique available in Java (versions 1.5 and above) and .net (version 2 and above).  The Generic Datatype  Overloading and polymorphism go a long way towards making an object oriented system work ‘properly’  But they only take you to the bridge.
  3. 3. The Problem  Let’s say we want to create a basic data structure.  One that works for any object.  It’s something straight forward – a queue, for example.  How do we do that?  Well, in older versions of a language we’d declare the data type as an Object.
  4. 4. The Queue  Public class Queue { ArrayList<Object> myObjects; void addToQueue (Object ob) { myObjects.add (ob); } void removeFromQueue () { Object ob = myObjects.get(0); myObjects.remove (0); return ob; } }
  5. 5. The Problem  We can use casting to turn whatever is on the queue into whatever class we need: Person p = (Person)queue.removeFromQueue();  This is bad OO.  It’s not type safe  We need to enforce discipline to make sure that we don’t put the wrong things on the wrong queues.  The compiler should be doing as much of that as is feasible.
  6. 6. The Solution  Both C# and Java offer the Generic as a solution.  A class which acts as a type-safe template.  The syntax is a little awkward, but it allows us to define the type that should be associated with a data structure.  Like we do with ArrayLists.
  7. 7. Queue – Java Generic import java.util.*; public class Queue<T> { ArrayList<T> myList; public Queue() { myList = new ArrayList<T>(); } public void addToQueue(T ob) { myList.add(ob); } public T getFromQueue() { T ob = myList.get(0); myList.remove(0); return ob; } public boolean hasMoreElements() { return (myList.size() != 0); } }
  8. 8. Queue – Java Generic public static void main(String args[]) { Queue<String> myQueue = new Queue<String>(); myQueue.addToQueue("Hello!"); myQueue.addToQueue("World!"); while (myQueue.hasMoreElements()) { System.out.println(myQueue.getFromQue ue()); } }
  9. 9. Queue – C# Generic class Queue<T> { ArrayList myObjects; public Queue () { myObjects = new ArrayList(); } public void addToStack<T>(T ob) { myObjects.Add(ob); } public T removeFromStack() { T ob = (T)myObjects[0]; myObjects.RemoveAt(0); return ob; } } }
  10. 10. Why Use Generics?  Type safe – we can ensure type incompatibilities are dealt with at the earliest possible opportunity.  Simplifies syntax – no need to cast individual objects.  Allows for effective deployment of certain kinds of design patterns.  Avoids the need for excessive specialisation of classes.
  11. 11. How do they work?  It is important to know the different ways in which variables are bound during the running of an application.  Traditionally variables are bound to a specific context in one of two ways.  Static binding, which is done at compile time.  Dynamic binding, which is done at runtime.
  12. 12. Static Binding  Explicitly indicating the type of a parameter allows for the compiler to link objects and variables when compiled.  They’re not going to change in that respect.  The performance of this is high, and compile-time checking can be rigorous in a way that’s not possible otherwise.
  13. 13. Late Binding  Late binding is used extensively in Java and C#.  One key area in which it is used is in polymorphism.  When you use Polymorphism, Java adopts a late binding approach so that it can properly adapt to the object at runtime.  It knows the most specialised method to use when invoked, but only when the object is bound.
  14. 14. Strongly Typed Languages  In strongly typed languages, early binding is the norm.  We can tell what the context is going to be by analysing the runtime  However, late binding needs to be dealt with either through polymorphism or compile time casting.  Generics allow for you to defer the binding of a data type until its point of usage arrives.  The <T> parameter is unbound.  When we instantiate the class, we bind it to a specific context.
  15. 15. Boxing and Unboxing  In both Java and C# a related mechanism is known as autoboxing.  This is the process of converting a value variable into an object reference, or vice versa.  When a value reference is boxed, it is stored on the ‘managed heap’.  A chunk of memory set aside and tended by the garbage collector.
  16. 16. Wrapper Classes  Each primitive data type in Java and C# comes with a corresponding wrapper class.  A class designed to provide a way of dealing with it as a reference.  It used to be impossible to have an ArrayList of ints in Java.  You needed to make them Integer objects first.
  17. 17. Wrapper Classes  Autoboxing then is the process at play when a primitive data type is encapsulated within a wrapper.  And vice versa, when it is unwrapped into its primitive form.  Autoboxing is a relatively expensive process.  If you were doing this a lot, it would be worth assessing your specific data manipulation requirements.
  18. 18. Generics and Constraints  All of this leads to an obvious problem.  What if we don’t want everything to be on the table for a generic?  Luckily, generics allow us to set constraints on them.  Limitations that restrict what can be a valid specification of our class.  There are six types of these in .NET.
  19. 19. Constraints Constraint Description Where T: struct The type argument must be a value type. Where T: class The type argument must be a reference type. Where T: new() The type argument must have a public, parameterless constructor Where T: <class name> The type argument must extend from the indicated class name. Where T: <interface name> The type argument must implement the specified interface, or be the interface itself Where T: U The type argument for T must be or derive from the argument supplied for U.
  20. 20. Example class Queue<T> where T : IComparable { ArrayList myObjects; public Queue () { myObjects = new ArrayList(); } public void addToStack<T>(T ob) { myObjects.Add(ob); } public T removeFromStack() { T ob = (T)myObjects[0]; myObjects.RemoveAt(0); return ob; } public Boolean isInQueue(T ob) { T ob2; for (int i = 0; i < myObjects.Count; i++) { ob2 = (T)myObjects[i]; if (ob2.CompareTo(ob) != -1) { return true; } } return false; } }
  21. 21. Constraints  Multiple constraints can be applied to the same parameter.  And in turn, they can be generic in and of themselves.  If you are going to be performing operations on a type that are not defined in Object itself, you need to apply a constraint.  That will allow for the method to be made available in a type-safe way.
  22. 22. Multiple Parameters  Some classes may provide two types.  For example, Hashtables  T and U are used conventionally to refer to parameter 1 and parameter 2.  You can apply separate constraints to each of these:  Where U : class Where T : iComperable
  23. 23. Unconstrained Types  With unconstrained types, we have the following restrictions:  We cannot use simple logical comparators on them, because there is no guarantee the concrete type will support them.  They will need to be formally cast.  You can compare to null, but this will always return false if the type argument is a value type.
  24. 24. Conclusion  Generics offer a new and powerful way to deal with type-safe collections.  And other kinds of classes.  Constraints allow us to ensure that we can access useful methods as required.  Polymorphism will ensure that we can reliably access whatever internals we require.  They’re available in both C# and Java.