What is generics?
Is a programming functionality which allows a type or method to operate on objects of various types while providing compile-time type safety. [1] Generics add stability to your code by making more of your bugs detectable at compile time. [2]The basic syntax in JAVA is <T extends MyClass>, where T is the generic variable to be used in the function implementation which in this case it will handle MyClass and all subclasses. Imagine you need a function to sort objects of MyClass type and all their subclasses. Without generics you will need a function per type. But thanks to generics you can have only one function to handle all subclasses.
Example:
Non-Generic | Generic |
public void MyFunction(MyClass aClass); public void MyFunction2(AnotherClass aClass); .... |
public <T extends MyClass> void MyFunction(T aClass); |
Duplicate code to handle each subclass | Only one function can handle all classes derived from MyClass |
For detail implementaion information:
http://docs.oracle.com/javase/tutorial/java/generics/index.htmlGenerics are useful for...
The power of generics can be appreciated when designing components that other developers will use. I find this specially important when you need to constrain the domain, but leaving it generic enough. So why constrain the domain if we are talking about generics usage? Because in my experience, developers take nasty shortcuts (supposedly to save time...) and use arguments type of strings or objects, or any wtfClass to hack there needs. Having a well define generic function will help other developers understand better the input of a method and avoid errors.Example: Generics and Design
In this example application we have an Activity which processes a ResultRes container. The only purpose of an Activity is to modify the values of the ResultRes object. You will see how using generics avoids unexpected errors and help developers understand better the classes needed to interact with an activity.Download: Source Code
Read the comments in the code for insight about the demonstration...
Parts of the application:
- ResultRes - is the generic results container.
- IntegerRes & StringRes - are sub-classes of ResultRes with different value types.
- ExecTemplate - is the generic class for activity execution.
Notice how the template definition is the main issue in the design. - ActivityInt & ActivityString - are implementations of the ExecTemplate.
- ExecGenerics - is just the Main() for testing the project.
Main Program - usage of generics...
package generics.main; public class ExecGenerics { /** * Example application about design and usage of generics. * The functionality is just to present how a background processing * can cause exceptions due to bad design implementation which could * been avoided. */ public static void main(String[] args) { boolean isUsingOriginal = true; // Set starter values... // This value will be modified by execute() IntegerRes i = new IntegerRes(); i.setFieldValue(123); StringRes s = new StringRes(); s.setFieldValue("123"); // print original values System.out.println(i.getFieldValue()); System.out.println(s.getFieldValue()); if (isUsingOriginal) { new generics.original.ActivityInt().execute(i); new generics.original.ActivityInt().execute(s); // -- ^^^ this is LEGAL & PROBLEM.. execution will fail! new generics.original.ActivityString().execute(s); } else { new generics.improved.ActivityInt().execute(i); // -- ^^^ watch how the helper shows the correct input class // -- ^^^ (Ctrl+Space) inside parenthesis // new generics.improved.ActivityInt().execute(s); // -- ^^^ this is NOT LEGAL NOW.. (won't compile) new generics.improved.ActivityString().execute(s); // -- ^^^ watch how the helper shows the correct input class // -- ^^^ (Ctrl+Space) inside parenthesis } // print values after processing System.out.println(i.getFieldValue()); System.out.println(s.getFieldValue()); } } |
Execution Results
original (isUsingOriginal = true) |
123 123 EXEC: START - class generics.original.ActivityInt EXEC: COMPLETE - class generics.original.ActivityInt EXEC: START - class generics.original.ActivityInt Exception in thread "main" java.lang.ClassCastException: generics.shared.StringRes cannot be cast to generics.shared.IntegerRes at generics.original.ActivityInt.performAction(ActivityInt.java:10) at generics.original.ExecTemplate.execute(ExecTemplate.java:11) at generics.main.ExecGenerics.main(ExecGenerics.java:32) |
improved (isUsingOriginal = false) |
123 123 EXEC: START - class generics.improved.ActivityInt EXEC: COMPLETE - class generics.improved.ActivityInt EXEC: START - class generics.improved.ActivityString EXEC: COMPLETE - class generics.improved.ActivityString 999 999 |
Execute Template - Generic template design...
original |
package generics.original; public abstract class ExecTemplate { protected abstract void performAction(ResultRes res); public void execute(ResultRes res) { System.out.println("EXEC: START - " + this.getClass().toString()); this.performAction(res); System.out.println("EXEC: COMPLETE - " + this.getClass().toString()); } } |
improved |
package generics.improved; public abstract class ExecTemplate<T extends ResultRes> { protected abstract void performAction(T res); public void execute(T res) { System.out.println("EXEC: START - " + this.getClass().toString()); this.performAction(res); System.out.println("EXEC: COMPLETE - " + this.getClass().toString()); } } |
Activity - No casting needed when well designed...
original |
package generics.original; public class ActivityInt extends ExecTemplate { @Override protected void performAction(ResultRes res) { IntegerRes r = (IntegerRes) res; // <-- Casting is required and not safe... r.setFieldValue(999); } } |
improved |
package generics.improved; public class ActivityInt extends ExecTemplate<IntegerRes> { @Override protected void performAction(IntegerRes res) { res.setFieldValue(999); // <-- No casting required and enforces type } } |
No comments:
Post a Comment