Skip navigation.
Home

Java 5.0 Generics Tip

Java 5.0 release introduced some very interesting features. Generic programming is probably the most important addition. This tip will try to introduce a useful use case using generics. Assume you are building a communication system, where lots of protocols are involved. Java already supports a wide group of communication protocols, but assuming you want to adapt all protocols needed by your system under a unified API, we need to do some work. So, what would we need for such API? Ok, a 'Protocol' contract might be useful. Oh, this 'Protocol' needs some kind of 'Configuration'. Let's start by a very simple 'Configuration' interface: {{{ public interface Configuration{ public Properties getProperties(); public void setProperties(Properties properties); } }}} This is a very simple contract that will allow you set and get some properties. But why this is needed? I can just use 'Properties' class directly. We will get to this shortly. Now, let's write our 'Protocol' interface: {{{ public interface Protocol{ public InputStream getInputStream(); public OutputStream getOutputStream(); public void setConfiguration(Configuration configuration); public Configuration getConfiguration(); public void close(); } }}} That's it?!! No, there is a simple enhancement that can be done with this code. Java allows you to override a method of a base class, old news. Ok, Java allows you to override a method of a base class and change the return type to a more specific one. So, when you implement the 'Protocol' interface, 'getConfiguration' method signature can specify a descendant of the 'Configuration' interface. Nice, can I do this with the parameters as well? No, you can't, but here comes the power of generics, let's enhance this code: {{{ public interface Protocol{ public InputStream getInputStream(); public OutputStream getOutputStream(); public void setConfiguration(C configuration); public C getConfiguration(); public void close(); } }}} Now, let's see a simple implementation of a protocol, let's say HTTP: {{{ public class HttpConfiguration implements Configuration{ protected Properties properties; public HttpConfiguration(){ properties = new Properties(); } public String getUri(){ return properties.getProperty("uri"); } public void setUri(String uri){ properties.setProperty("uri", uri); } public Properties getProperties(){ return properties; } public void setProperties(Properties properties){ this.properties = properties; } } }}} Notice how the getters and setters can use the properties to simplify access, for example, we can add a 'getUrl' method, which creates a URL instance from a string URI and handles the thrown exceptions for us. Ok, let's see an HTTP implementation of our 'Protocol' interface. {{{ public class HttpProtocol implements Protocol{ protected HttpConfiguration configuration; public InputStream getInputStream(){ InputStream in = null; //initialize the stream, open a session with the remote server return in; } public OutputStream getOutputStream(){ OutputStream out = null; //initialize the stream, blah, blah return out; } public void setConfiguration(HttpConfiguration configuration){ this.configuration = configuration; } public HttpConfiguration getConfiguration(){ return configuration; } public void close(){ // clean up } } }}} Notice how the configuration setter and getter are more specific to their related 'HttpConfiguration' implementation. Yes, we don't need to cast anymore. Now if you start implementing lots of protocols this way, we might need a factory to support multiple implementations, let's start an abstract factory: {{{ public abstract class AbstractProtocolFactory

{ public abstract P createProtocol(C Configuration); } }}} This factory will allow the creation of a 'Protocol' instance using a 'Configuration' instance; let's implement this for our HTTP protocol: {{{ public class HttpProtocolFactory extends AbstractProtocolFactory{ public HttpProtocol createProtocol(HttpConfiguration configuration){ HttpProtocol http = new HttpProtocol(); http.setConfiguration(configuration); return http; } } }}} This is a specialized version of the generic factory, if you have multiple implementations of the HTTP protocol, this factory will allow choosing an implementation. Here, we are just picking our sample implementation. A sample usage of this API might look like this: {{{ public class Sample{ public static void main(String...args) throws Exception{ HttpProtocol http = new HttpProtocolFactory().createProtocol(new HttpConfiguration()); http.getConfiguration().setUri("http://www.egjug.org"); InputStream in = http.getInputStream(); if(in != null){ // read Egypt JUG main page } http.close(); } } }}} Ok, That's it. One can find designing API using generics really interesting.

Nice external comment

I just read some nice comments about my post here at EGJUG. The poster has very valid points.\\ The comments are posted here:\\ http://javachaos.crazyredpanda.com/?p=126 \\I would like to add a comment about post-deployment configuration:\\ As I said in my post, this kind of factory can provide instances of multiple implementations of the SAME protocol. For example: {{{ public HttpProtocol createProtocol(HttpConfiguration configuration){ HttpProtocol http = null; try{ Class protocolClass = (Class) Class.forName("org.egjug.tips.generics.HttpProtocol"); http = protocolClass.newInstance(); http.setConfiguration(configuration); } catch(ClassNotFoundException e){ e.printStackTrace(); } catch(IllegalAccessException e){ e.printStackTrace(); } catch(InstantiationException e){ e.printStackTrace(); } return http; } }}} Or in general:\\ {{{ public HttpProtocol createProtocol(HttpConfiguration configuration){ HttpProtocol http = null; try{ Class protocolClass = (Class) Class.forName(System.getProperty("org.egjug.tips.generics.protocol.http")); http = protocolClass.newInstance(); http.setConfiguration(configuration); } catch(ClassNotFoundException e){ e.printStackTrace(); } catch(IllegalAccessException e){ e.printStackTrace(); } catch(InstantiationException e){ e.printStackTrace(); } return http; } }}} I totally agree with all other comments, and will try to update the post later ISA.