generics : Java Glossary


generics  generics
Generics allow type checking to make sure you fill your Collections and ArrayLists with only the types of Object you intended. Further, they check that you are using the right sort of Comparator for the objects your are sorting. This type checking happens mostly at compile time. This discussion is aimed at the beginner or intermediate user. For the advanced fine points, see some of the other tutorials linked below.
Introduction Static Methods Under the Hood
Learning Strategy Static Factories Converting Sets
Why? Static Inference Tips
The Ugly Serialized Objects Books
Using Generics <?> vs <Dog> vs <? extends Dog>… Learning More
Comparator/Comparable Lower and Upper Bounds Links
Iterator Mixing Generics and Arrays

Introduction

Generics are an advanced feature of Java 1.5+. C++ has something similar called templates. You instantiate versions of classes using some type as a parameter, e.g. a stack of ints and or a stack of floats or a stack of Objects. In most other languages from Sather to Ada, templating is called genericity or parameterised types.

All the Java Collection classes, e.g. HashMap, ArrayList, TreeList can contain only generic Objects. The problem with this is you must keep track manually what sorts of object are really in each Collection. The compiler won’t warn you if you add a Cat to a Collection of Dogs.

Without genericity, in Java, you must, at run time, cast objects on the way into a Collection to make sure they are the right type, and cast them back again on the way out before you can use their methods. It is totally up to you as application programmer to enforce your rules about what sort of objects you want in each Collection. You can’t simply declare that a Collection will contain only Dog objects, and have attempts put anything else in flagged at compile time.

Java version 1.5 or later has generics implemented with high overhead, requiring implicit casts both on the way into the collection and on the way out. The compiler automatically casts all objects going into your collection to Dog, and will automatically cast them back to Dog on the way out. Inside the collection, they are treated as generic Objects.

In theory, neither of these two time consuming casts are necessary. However, removing them would create a vulnerability in the JVM (Java Virtual Machine) that could be exploited maliciously. The saving grace of the design in that Sun did not need to change the JVM or class file structure to implement it.

You can try out the Java version 1.5 or later compiler with generics.

Learning Strategy

Generics are by far the most difficult to understand feature in Java. Here are some hints to make learning them easier.

Why The New Syntax?

You might think you could have just passed types as parameters to methods the way you can now, using names like Class. Dog. However, generic types don’t apply to individual methods, but to the whole instantiation of a class. The actual types are decided at the time the class is instantiated, and meaning of type T is the same for all methods. The type can’t be decided at method invocation time. In other words you can’t have a generic typed method except inside a generic typed class. Generics are similar to Class parameters passed to a constructor, but they imply much more rigid structure. Most of the time, the parameterised types just constrain the types held inside a generic container some sort.

Further, types passed to a static method define the types of the parameters in the call, something quite different from passing a class as a parameter, which could only have an effect once the call had been made, e.g.

The theoretical magical property of the new syntax is that users of new generified classes need not change their source code, or even recompile. This was done so that Java version 1.5 source could run on old 1.4 JVMs (Java Virtual Machines). Generics jumped through hoops to maintain compatibility with the old JVMs and class files. However, in practice, for other reasons, it turns out Java version 1.5 code won’t run on old JVMs. If you do decide to use generics in your code that calls the new generic Collection classes for example, you only need to change your variable declarations and where you instantiate objects. All the other code can stay identical, though many casts then become unnecessary. You really only need to understand generics to any extent when you write your own parameterised classes, usually containers of some sort. There is nothing to do for code that uses those classes, other than fix the errors the type checking uncovers.

The Ugly

I usually start to splutter with rage at trying to solve generics problems. Generics are supposed to make coding safer not more hideous and complicated. There is something fundamentally wrong with generics when it starts requiring intricate gavottes.

How To Use Generics

You

javac.exe -source 1.5 -target 1.5 *.java

to turn on generics. Otherwise they will be treated as syntax errors.

To create an ArrayList that can only hold Dog Objects. If you attempt to put anything else in there, (except subclasses like Dalmatians), you will find out at compile time. This is a great improvement on the old days when you sometimes found out about your errors when you partly exercised the code at run time.

This is all very well for container classes that treat their contents as simple Objects and never execute any methods on them. But what if you had a smart container that wanted to run some of the Dog methods on its contents? Then you would have to say something like this where D stands for the class of the variable type of the container. You define your new Kennel container that can only hold Dogs or their 
// this is NOT legal
java.util.Vector<Dog> dogs = new java.util.Vector<Dalmatian>();

Comparator/Comparable

Iterator

Generics and Static Methods

Generics and Static Factories

Sooner or later you will try to write some generic code that creates a new Object of type T where T is a generic type parameter of the class or static method. You might naïvely try this:
// naive and unsuccessful attempt to create a new object of generic type T
T thing = new T();
This fails because of type erasure. All trace of generics disappears at run time leaving just raw objects. The runtime 
// What that code compiles to at run time:
Object thing = new Object();
To get the effect of being able to create an object of an arbitrary class you need to pass a Class Object parameter and use reflection with newInstance. Further, your construction classes need to have no-argument constructors. Generics are extremely subtle and difficult. One shortcut is to think of a Sun class that has similar generics to one you are writing and have a peek at the source code in src.zip to see how Sun pulled it off, e. g. If you wanted to use an enum as a generic qualifier, see how EnumSet did it. To see how to allocate an array of a generic type T, see how ArrayList did it. See Angelika Langer’s Generics FAQ for details on how the kludges work.

Static Inference

If you look at the  Collections.sort method 
// How the sort method is defined in Collections.sort
// Quite a bit more complex that using it, eh?
public static
  <T extends Comparable<? super T>>
  void sort( List<T> list )
The sort static method is designed to work with a class T that implements Comparable on T or some superclass of T, e.g. Object. (Infuriatingly, generics use the term extends when they really mean implements). The method wants to sort a List of such T class objects.

You would expect to invoke it then with:

However, the compiler is smart enough most of the time to guess the type(s) from the context. In this case the compiler knows that listOfDogs is a List< Dog> therefore the mysterious class T must be Dog. The compiler gets clues from the types of the parameters and suprisingly from the type on the left hand side of the equal sign which it matches with the return type allowing you to get away with the short form:
// Short form of sort invocation
// relying on compiler static type inference
Collections.sort( listOfDogs );

So when is Java smart enough to figure out the types on its own via inferring? It sometimes feels as if it depends on the phases of the moon. Inferring has even the cleverest programmers scratching their heads. Nobody can figure out for certain why sometimes the compiler can infer the generic types and sometimes it cannot. So what can you do?

If you have Eclipse, you can rapidly experiment, removing the explicit types one by one and putting them back if Eclipse complains. If you don’t have Eclipse, you can do the same thing more slowly with a compile cycle.

The problem with removing type tags is if you remove one too many tags, you are flying without a net. You have effectively turned off type checking. It is difficult to tell the difference between allowing inference to check types and turning off type checking altogether. This is a serious flaw in the design of Java generics.

Generic type tags are useful documentation to let others know what your code is up to. That is an argument for leaving them in.

If you burn the generic types into your code in 100 places, it will be hard to modify later if you change that type. Ideally you should specify the type of a variable in only one place — where it is declared. That is an argument for removing the type tags wherever you can.

Generics and Serialized Objects

Code like this will cause problems:

// generates type mismatch error
ArrayList<Thing> things = ois.readObject();

You can try to fix it with a cast like this:

// generates cast warning
ArrayList<Thing>things = (ArrayList<Thing>)ois.readObject();

but that generates a warning message. The problem is type erasure. The generic type is not stored with the serialized object. Java could check that the Object read back was an ArrayList, but not that it was an ArrayList< Thing>. Since it can’t guarantee, it gives the warning. The problem is the lame type erasure way generics were implemented. Had the type information been included, Java could check and the cast would be valid. So what do you do? You can just live with the warning, suppress it with an annotation like this:

// suppress unchecked warning
@SuppressWarnings( "unchecked" )
void restore()
   {
   // Java cannot check that the object is actually an ArrayList<Thing>.
   // It will just trust that it is.
   ArrayList<Thing> things = (ArrayList<Thing>) ois.readObject();
   ...
   }

or you can copy the fields one by one like this:

If your ArrayList is already allocated and final, you can do it this way:

// copy with a temporary array, generates no warning
final ArrayList<Thing> things = new ArrayList<Thing>( INITIAL_SIZE );
...
final ArrayList<Object> temp = (ArrayList<Object>)ois.readObject();
things.clear();
for ( Object item : temp )
   {
   things.add( (Thing)item );
   }

It is much simpler to read and write serialized arrays than ArrayLists. They don’t have this problem since you are not relying on generics for your type information. For arrays, the Java type system embeds the actual type in the ObjectStream. The problem is you may not be able to switch your ObjectStream ArrayLists to simple arrays when your clients have many files in the old serialized ArrayList format. Arrays are also slightly more compact.

Syntactic Soup: <?> vs <Dog> vs <? extends Dog> vs <E extends Dog> vs <? super T>

The basic problem is that ArrayList<Dalmatian> is not a subclass of ArrayList<Dog>. To get around that, some quite bizarre and non-intuitive syntax had to be dreamed up to let you write code that could handle either sort of Collection with the same method and still have some type checking.

ArrayList<Dog> is a collection permitted to contain Dogs or any subclass of Dog. An ArrayList< Dog> that incidentally contained only Dalmatians is a quite different beast from an ArrayList< Dalmatian> which can’t contain anything but Dalmatians (or their subclasses).

Collection<Object> is a heterogenous Collection, while Collection<?> is a homogenous Collection of elements of the same unknown type, e.g. Dog and its subclasses.

Consider the wildcard form of a type specification:

Collection <? extends T >
vs the simple form:
Collection < T >
What matches if T is Dog?

The wildcard form restricts our ability to put raw Dog objects into the Collection, but on the other hand it lets us deal with Collection< Dalmatian>, where we could not otherwise.

You will need to read Oracle’s tutorial many times. It is as subtle as a Buddhist sutra. It makes me wonder if generics were a big mistake, introducing something so very difficult to solve a simple problem. Angelika Langer’s Generics FAQ is somewhat more accessible. Just keep reading and experimenting and more and more of what you read will make sense little by little. Unfortunately, I find the understanding does not stick and you have to keep going back to the well to have it continue to make sense.

Here is a complex example. If you understand this, you will be well on your way to understanding the fine points:

public static
<D extends Dog & Comparable<? super D>>
D selectSuitableDog( Collection<? extends D> );
Means:

This static method above is designed to be used with class D that has the following properties:

The method selectSuitableDog will return an object of class D.

You must pass a Collection to the method where the Collection contains Dog objects (where the Dog objects may be subclasses of Dog), or a more limited Collection of some subclass of Dog objects, e.g. a Collection< Dalmatian> of pure Dalmatian objects. Note that a Collection< Dog> where all elements happen to be Dalmatian is quite a different animal from a Collection< Dalmatian>where all elements are coerced to be Dalmatians (or subsets of Dalmatian).

Compared with the gobbledegook to define the method, your call to the method is very simple:

Lower and Upper Bounds

This section was written by Joshua Cramer, in response to my challenge to explain generics lower and upper bounds in a straightforward way. It as yet another attempt at explaining the syntactic soup I tried to explain in the preceding section. My comments are in green.

It helps to understand generics by thinking in terms of containers — most generified types are containers/Collections in some form or other.

Generified

public class Container<T> {}
The T represents a type. The implementer of Container has no idea what type it is, it’s just some type, any type. Since we don’t know what it is, all we can guarantee is that it is an Object of some type (all non-primitive types are a subclass of Object, and generics don’t use primitive types), so wherever T is found in the implementation, you can mentally replace it with Object for the same effect.

Upper Bounds

Sometimes, though, we don’t want to let people include any old object. To do this, we declare an

public class Container<T extends Number> {}

If you think of a class hierarchy with the basic root classes at the top and the subclasses dangling down from it, then the upper bound is the name of the root uppermost class of a branch that is valid for T.

Now only Numeric types can be stored in the container. Also, since we know that any given value of T has to be a subclass of Number, we can safely reference objects in the container as Numbers instead of Objects.

Lower Bounds

You can also specify a lower bound on the type, by specifying super instead of extends. The rationale for why you would want to specify a lower bound should come clear later.

Again, if you think of a class hierarchy with the basic root classes at the top and the subclasses dangling down from it, then the lower bound is the name of the lowermost class in a chain of superclasses that is valid for T. The bound includes the class mentioned, not just its super classes.

static <T> void sort( List<T> list, Comparator<? super T> c )

The above example says that for example, you could sort a List< Dalmatian> with either a Comparator< >Dalamatian> or a Comparator< Dog>.

To use generics, you merely have to replace the appropriate type parameter with your class. So a Container for Integers is declared as Container< Integer> (again primitive types are not valid here; this is due to implementation necessities.

Happily Java version 1.5 or later autoboxes primitive types in method calls allowing you to simulate containers of primitives.

Subclassing

An important fact to realise is that subtyping of generic types isn’t quite what would be expected at first: if A is a subclass of B, Container< A> is not a subtype of Container< B>. You can store any A into a Container< A>, so if B is a subclass of A, besides an A you can also put a B into Container< A>.

Wildcard Types

The way around this is to state that you don’t know what’s contained inside in the container. We do this with the type ? (also known as the wildcard type): Container<?> stores something, but we don’t know what. Since Container< ?> stores objects of any type, in particular it stores objects of type A. Thus Container< A> is a subtype of (and thus convertible to) Container< ?>.

Conversely, since we don’t know what type a Container< ?> holds, we can’t store stuff inside the container.

Otherwise we might accidentally store an Object in the Container of a type that does not belong.

However, since we know that all Containers must store Objects, we can treat whatever is in the Container as an Object. In effect, the wildcard turns the Container into a read-only object: you can’t write into Container< ?>, but you can read from it. This is internally enforced by the special subtyping rules of generic objects; the exact mechanisms require some mathematical theory that I will not delve in here.

Similarly to how we limited the storage type of a container with the extends Number relationship, we can limit the type of a wildcard. A type Container<? extends Number>' indicates that the object stores something that is at least of type Number. Once again, that means that instead of viewing it as containing something that is at least an Object, we can see it as containing something that is at least a Number.

Keep in mind the difference between a <Number> container that just incidentally contains only Integer objects, and an <Integer> container that is restricted by generic type constraints to only Integer objects.

Recursive Types

Types can also be recursive. A recursive type is something of the form:
public class Foo<T extends Foo<T>> {}

You will see recursive types used in the definition of the Enum class that underlies enums.

However, be careful not to think of the generic parameter declaration as any kind of operation.

public class Foo<T extends Foo<T>> {}
is not an operation. It is an assertion about the limitations on the type.

You generally subclass or implement such

public class Bar extends Foo<Bar> {}

The primary purpose of a recursive type is to allow the creation of generic interfaces whose methods require the use of the implementing class as parameters.

The other main use of lower bounds is in recursive types. Specifying

<T extends A<T>>
is 
<T extends A<? super T>>
is typically more correct.

Generifying Methods

Methods can also be generified. The rationale here is for utility methods operating on generic container classes. Here 
public static <T> T move( Container<T> from, Container<? super T> to);
Here, you also saw an example of the use of the lower bounds. We want to be able to take an Integer stored in a Container< Integer> and put it into a Container< Number>. Using upper bounds is also possible here, but if we were to do that, you would extract a Number and not an Integer from the container. The object being moved is already known to be a Number, so we should we unnecessarily restrict the type of return?

Mixing Generics and Arrays

This material comes compliments of Lew, a frequent contributor to comp.lang.java.programmer. The comments in green are mine.

Generics and arrays do not mix well. That’s because arrays remember their underlying type at runtime, but generics just become plain Objects at runtime through the process of type erasure†.

The compiler will not let you create an array of generic types, unless the generic parameter comprises entirely unadorned wildcard (?) characters. So

// legal
Foo<?> [] bunchaFoos = new Foo<?> [NUMFOOS];

is legal, but

// illegal
 Foo<Bar> [] bunchaFoos = new Foo<Bar> [NUMFOOS];

is not. Other things like casting and reflection get really difficult, too.

For almost everything you want to do mixing arrays and generics you can use ArrayList instead of an array. The syntax is a little more verbose, and the code is somewhat slower, but the type safety and expressiveness compensate.

† In technical terms, an array is a reifiable type — in that it can be made real in the JVM. Consequently its base type must also be reifiable. A generic type, except for the pure wildcard ? generics, is not reifiable because of erasure. So the compiler won’t let you make an array of a generic type.

If you look at Oracle’s code for ArrayList and other generic-supporting classes, you might be surprised to find code that generates unchecked warnings (generics violated). There is no legitimate way to allocate arrays of generics. So what we do is sweep this unclean code under the rug, and let ArrayList do the dirty deed for us. At least our code is clean.

The popular wisdom is you can’t have an array of HashMaps. Actually, you can, it is just it is not kosher generics.

// HashMap kludge. This will compile.
HashMap<Long,String> hashMaps = new HashMap[ 1000 ];

// you may not write
HashMap<Long,String> hashMaps = new HashMap<>[ 1000 ];

// or
HashMap<Long,String> hashMaps = new HashMap<Long,String>[ 1000 ];

Mark Vedder made a stab at explaining why: In Java, arrays are covariant but generics are not. Array covariance has been present since Java version 1.0. When generics were added in Java version 1.5, they were made invariant since generics were implemented to ensure type safety at compile time. Given that arrays are covariant, that is possible that not all slots of the array will contain identical types as long as the type put into the slot inherits from the type the array represents. For example, putting a String and a Date in an Object[] array, or a String and a StringBuilder in a CharSequence[] array. If the type put into the array is not in the type hierarchy of the array, for example putting a Date into a String[] array, an ArrayStoreException is thrown.

Oracle’s Javadoc on ArrayStoreException class : available:

This is a Runtime exception. So the issue is not caught until Runtime. With generics, it would be caught at compile time.

What are you supposed to do instead of using an array of HashMaps?

Use a List<Set<YourType>> backed by an ArrayList of HashMaps.

I think he meant Map not Set.

I have read these explanations many times, and I still don’t get it. It is not so much how it works, but why syntax whose meaning seems clear to humans does not work. Why is it broken? Was it really impossible to do properly? I suspect some of these strange properties of generics came from going for a simple implementation (avoiding any run time implementation), rather than a logical one. Or it may have happened because generics were tacked on later to Java’s arrays. Arrays are not considered Collections, even though they share many characteristics.

Under the Hood

Most of the quirky features of generics derive from the fact they are a purely compile time feature.
The general rule is, don’t try to make generics enforce conditions not known 100% at compile time, e.g. the properties of dynamically loaded code, or deserialized streams. Only non-generic code can check at run time if those expected conditions are truly met.
They leave almost no trace in the run time code. The fancy name for this is type erasure. If you decompile ArrayList for example which is crawling the generics in the source, there is no sign of the generics in the class file. It just stores and retrieves ordinary Objects without any casting. The only sign that generics were ever involved are the casts automatically inserted on the gets in the caller to convert the Objects to the types of receiving variables. There is no casting code in ArrayList itself. This is why generic code can’t do obvious things like construct an object of type T or cast an object to type T. There is no type T in the runtime code unless you use special measures described in Angelika Langer’s Generics FAQ. Type erasure has a number of annoying consequences, but it has one big advantage. It is possible to write a Collection with generics that old non-generic  Java version 1.4 class files can still use, by ignoring the generics. Without type erasure, to allow old code to run in Java 1.5+, Sun would have had to create a new version of each Collection with generics and leave the existing one without generics for legacy use.

Converting Sets

Let us say you had a Set of Dalmatians each of which implemented the Dog interface, and your method wanted a Set of Dogs, not a Set of Dalmatians. What do you do?

Tips

Books

book cover recommend book⇒Learning Java, fourth editionto book home
by Patrick Niemeyer and Jonathan Knudson 978-1-4493-1924-3 paperback
publisher O’Reilly recommended 978-1-4493-7249-1 eBook
published 2013-06-25 B00DDZPC9I kindle
Covers Java 1.7 though nearly all of it is about older features, including enums and generics. Covers serialisation vs code generation. Particularly good at explaining the use of the Java 1.2 Collection classes. Teaches with example code, my favourite technique. It has a tiger on the cover because Oracle’s code name for Java 1.5 was Tiger. Contains a very good chapter on generics. The book is not for people new to programming. This is the book Mark Space recommends, especially to learn OO programming.
Australian flag abe books anz abe books.co.uk UK flag
Chinese flag amazon.cn amazon.co.uk UK flag
German flag abe books.de abe books.ca Canadian flag
German flag amazon.de amazon.ca Canadian flag
Spanish flag amazon.es Chapters Indigo Canadian flag
Spanish flag iberlibro.com abe books.com American flag
French flag abe books.fr amazon.com American flag
French flag amazon.fr Barnes & Noble American flag
Italian flag abe books.it Google play American flag
Italian flag amazon.it O’Reilly Safari American flag
India flag junglee.com Powells American flag
UN flag Kobo other stores UN flag
Greyed out stores probably do not have the item in stock. Try looking for it with a bookfinder.
book cover recommend book⇒Effective Java: second editionto book home
by Joshua J. Bloch 978-0-321-35668-0 paperback
birth 1961-08-28 age: 52 978-0-13-277804-6 eBook
publisher Prentice Hall 978-0-13-715002-1 WebBook
published 2008-05-28 B000WJOUPA kindle
No design patterns, just generic advice on good Java programming style. This is considered the best explanation of generics, even though it has just one chapter on generics. People claim it all came clear after reading his explanation. It is also consider the best explanation of serialization. Not to be confused with his earlier Effective Java Programming Language Guide. book website
Australian flag abe books anz abe books.co.uk UK flag
Chinese flag amazon.cn amazon.co.uk UK flag
German flag abe books.de abe books.ca Canadian flag
German flag amazon.de amazon.ca Canadian flag
Spanish flag amazon.es Chapters Indigo Canadian flag
Spanish flag iberlibro.com abe books.com American flag
French flag abe books.fr amazon.com American flag
French flag amazon.fr Barnes & Noble American flag
Italian flag abe books.it Google play American flag
Italian flag amazon.it O’Reilly Safari American flag
India flag junglee.com Powells American flag
UN flag Kobo other stores UN flag
book cover recommend book⇒Just Java 2, sixth editionto book home
by Peter van der Linden 978-0-13-148211-1 paperback
publisher Prentice Hall 978-0-13-700990-9 eBook
published 2004-07-01 978-0-13-493982-7 audio
  B001MDC0FW kindle
Covers Java 5 aka 1.5. Peter has a sense of humour and breaks the drudgery of reading with a funny story at the end of each chapter. He explains through simple examples. This is a book you can sit down and read and not fall asleep. I helped edit and proofread the chapters on enums and genericity. This is good introduction that won’t overwhelm you.
Australian flag abe books anz abe books.co.uk UK flag
Chinese flag amazon.cn amazon.co.uk UK flag
German flag abe books.de abe books.ca Canadian flag
German flag amazon.de amazon.ca Canadian flag
Spanish flag amazon.es Chapters Indigo Canadian flag
Spanish flag iberlibro.com abe books.com American flag
French flag abe books.fr amazon.com American flag
French flag amazon.fr Barnes & Noble American flag
Italian flag abe books.it Google play American flag
Italian flag amazon.it O’Reilly Safari American flag
India flag junglee.com Powells American flag
UN flag Kobo other stores UN flag
book cover recommend book⇒Java Generics and Collectionsto book home
by Maurice Naftalin & Philip Wadler 978-0-596-52775-4 paperback
publisher O’Reilly recommended 978-0-596-55150-6 eBook
published 2006-10-18 B0026OR2HM kindle
Covers both generics and Collections. Covers Java 1.5+ features such as autoboxing, for:each as well. Recommended by Mike Schilling.
Australian flag abe books anz abe books.co.uk UK flag
Chinese flag amazon.cn amazon.co.uk UK flag
German flag abe books.de abe books.ca Canadian flag
German flag amazon.de amazon.ca Canadian flag
Spanish flag amazon.es Chapters Indigo Canadian flag
Spanish flag iberlibro.com abe books.com American flag
French flag abe books.fr amazon.com American flag
French flag amazon.fr Barnes & Noble American flag
Italian flag abe books.it Google play American flag
Italian flag amazon.it O’Reilly Safari American flag
India flag junglee.com Powells American flag
UN flag Kobo other stores UN flag

Learning More

Oracle’s Technote Guide on Generics : available:
Oracle’s Javadoc on Class.asSubclass : available:
Oracle’s Javadoc on Enum, the class underpinning enums : available:
Oracle’s Javadoc on Collections.checkedSet : Allows run-time checking that all elements added to a Set are of the correct type. : available:

This page is posted
on the web at:

http://mindprod.com/jgloss/generics.html

Optional Replicator mirror
of mindprod.com
on local hard disk J:

J:\mindprod\jgloss\generics.html
logo
Please the feedback from other visitors, or your own feedback about the site.
Contact Roedy. Please feel free to link to this page without explicit permission.
Blog
IP:[65.110.21.43]
Your face IP:[54.196.198.241]
You are visitor number