Skip to main content
gamedev.net
🔒 Locked

[java] Generics

Started by Son of Cain 12 replies 1.6k views
Original Post
Son of Cain
Son of Cain
I switched to J2SE 1.5, and got a few question about the generics... I want to hear your opinion about how can it improve game programming in java - if it will. If there's already a specific paper (tutorial, docs, whatever) on generics + game programming, I would appreciate the suggestion. =) Thx!
a.k.a javabeats at yahoo.ca
capn_midnight
capn_midnight
the main use of generics is just to avoid the problems of constantly upcasting objects that are stored in the java.util collections. For example, with an ArrayList, you add objects to it, but when you get objects back out, in pre-1.5 you would have to cast it back to the object you wanted. This is a problem if, for some dumb, reason you added objects of different types to the same list, and then tried to cast an object to a different type than it originally was.

Generics eliminate the need for casting, and add a wee bit of type safety, and that's basically it. It's mostly just for code cleanliness, and doesn't really add anything that can enhance a game, specifically.
Arild Fines
Arild Fines
Quote:
Original post by capn_midnight
Generics eliminate the need for casting

Java generics don't really do that. They merely hide the casts. For any given instantiation of a generic class, the Java compiler substitutes Object for the type parameters and inserts the necessary casts.
--AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.[Project site] [IRC channel] [Blog]
seanw
seanw
Quote:
Original post by Arild Fines
Quote:
Original post by capn_midnight
Generics eliminate the need for casting

Java generics don't really do that. They merely hide the casts. For any given instantiation of a generic class, the Java compiler substitutes Object for the type parameters and inserts the necessary casts.


They eliminate the need for explicit casting like the way "while" loops eliminate the need for explicit "goto" commands. It doesn't matter what the compiler is actually doing underneath the hood, just that you can use a convenient language feature knowing that the compiler guarantees it will execute in a safe and defined way. Your comment is accurate but it's like saying C++ templates are just macros.
Arild Fines
Arild Fines
Quote:
Original post by seanw
Your comment is accurate but it's like saying C++ templates are just macros.

It is pertinent because the implementation specifics in this case actually impacts the usability and performance aspects.
--AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.[Project site] [IRC channel] [Blog]
seanw
seanw
Quote:
Original post by Arild Fines
Quote:
Original post by seanw
Your comment is accurate but it's like saying C++ templates are just macros.

It is pertinent because the implementation specifics in this case actually impacts the usability and performance aspects.


I've come across cases where I was stung when I tried to use a static field with a generic type, but I've found generics in Java very useful and overall I am quite happy with them. C++ templates also have issues, such as increases in compiled code size and poor compiler support in some cases. Do you have any examples where Java generics impact usability or any data to suggest they cause performance problems?
Arild Fines
Arild Fines
Quote:
Original post by seanw
Do you have any examples where Java generics impact usability or any data to suggest they cause performance problems?

* You are unable to reflect over a generic type instantiation and retrieve the type parameters.
* Any use of primitives as a type parameter causes autoboxing and -unboxing, which are expensive operations.
* Type casts are runtime checked, and the jitter isn't able to optimize them all out.
--AnkhSVN - A Visual Studio .NET Addin for the Subversion version control system.[Project site] [IRC channel] [Blog]
seanw
seanw
Quote:
Original post by Arild Fines
* You are unable to reflect over a generic type instantiation and retrieve the type parameters.


Could you give an example and why this would significantly impact your programming? I'm not completely sure what you mean.

Quote:

* Any use of primitives as a type parameter causes autoboxing and -unboxing, which are expensive operations.
* Type casts are runtime checked, and the jitter isn't able to optimize them all out.


I'm aware of these, but have you had actual experiences or know of anywhere these have actually caused performance problems? You'd have to do the above operations manually anyway if you didn't use generics so they really aren't an issue.

I know Java templates aren't as powerful (or complex) as C++ templates, but they are still extremely useful and can be used to produce very clean and readable code. I used to use complex C++ templates extensively but Java code has a nice simplicity about it which I like and the introduction of generics has just made this better.
capn_midnight
capn_midnight
Quote:
Original post by Arild Fines
Quote:
Original post by capn_midnight
Generics eliminate the need for casting

Java generics don't really do that. They merely hide the casts. For any given instantiation of a generic class, the Java compiler substitutes Object for the type parameters and inserts the necessary casts.
I know the casting is still going on, I only care about whether or not *I* have to do it. It's really quite rediculous that all this time *I* had to do it, when the jre knows much better than I do what the type of the object is.

Anyway, I think the most important feature is that it adds some type safety. No longer can you do this:
    Integer i=new Integer(5);    Float f=new Float(5.5);    ArrayList l=new ArrayList();    l.add(i);    l.add(f); //wooo! 

I now have two different types of objects *in the same collection!* Thanks java, for making this possible. I'm going to Disney World!

Now, I know and you know that was a pretty stupid thing to do, but we know Sun just loooves to eliminate the possibility of doing stupid stuff (*cough*operator overloading*cough*anonymous innerclasses).
andrewk3652
andrewk3652
in case anyone else is curious (like I was), Serializable objects - like ArrayLists - will still read/write correctly with generics, as the following code shows:

import java.util.*;import java.lang.*;import java.io.*;public class SerGenTest implements Serializable {        public static void main(String[] args)        {                ArrayList<String> theList = new ArrayList<String>();                theList.add("Hello");                theList.add("How are you");                try {                        ObjectOutputStream out = new ObjectOutputStream(                                new FileOutputStream("theList.data"));                        out.writeObject(theList);                        out.close();                }                catch (IOException e)                {                        return;                }                theList = null;                try {                        ObjectInputStream in = new ObjectInputStream(                                new FileInputStream("theList.data"));                        theList = (ArrayList) in.readObject();                        in.close();                }                catch (IOException e) {                        return;                }                catch (ClassNotFoundException e)                {                        return;                }                for ( int c = 0; c < theList.size() ; c++ )                {                        System.out.println(theList.get(c));                }                return;        }}


So that much, at least, we know works.
=========================Buildium. Codium. Fragium.http://www.aklabs.net/=========================
CaptainJester
CaptainJester
Quote:
Original post by Arild Fines
Quote:
Original post by seanw
Do you have any examples where Java generics impact usability or any data to suggest they cause performance problems?

* Type casts are runtime checked, and the jitter isn't able to optimize them all out.

With generics the checks are compile time, not runtime.
Quote:
From New Features and Enhancements J2SE 5.0
Generics
This long-awaited enhancement to the type system allows a type or method to operate on objects of various types while providing compile-time type safety. It adds compile-time type safety to the Collections Framework and eliminates the drudgery of casting. Refer to JSR 14.
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][
SamLowry
SamLowry
Generics overview

AFAIK, type checks occur both at compile time and runtime. The VM was not modified to support generics, so it does not know if code has been verified at compile-time for type-safety, hence it must check anyway.

When using Java generics, you have to keep in mind that all your code will be compiled to 1.4-compatible code, meaning no generics, just objects, and no extra information anywhere. This leads to a few limitations.

For example, the following code does not work
class ArrayCreator<T> {    public T[] create(int size)    {        return new T[size];    }}


because when compiled, the T parameter just disappears, and the resulting code would not know which type to create. Look at the two versions of List.toArray

As Arild said, "You are unable to reflect over a generic type instantiation and retrieve the type parameters."
I believe he means that you can't use the parameter in your code for reflection (or for any other purpose). I wanted to make some sort of "Observer manager" class, which would take the observer interface as a generic parameter; using reflection it would retrieve the public methods and allow users of the manager to dispatch calls automatically to all registered observers in a safe manner (e.g. taking into account the fact that the observer list might change while notifying the observers, etc.)

ObserverManager<IAction> mgr = new ObserverManager<IAction>();mgr.register(observer1); // and so onmgr.notifyObservers("methodName", arg1, arg2, ...);


(don't tell me about Observable. I don't understand why anyone would just throw away type-safety like that, you might just as well make Java a dynamically typed language)

Again, such code is impossible:
// How to get access to the methodsMethod[] methods = T.getMethods(); // ?Method[] methods = T.class.getMethods(); // ?Method[] methods = new T().getClass().getMethods(); // ?


Nothing works. A simple (working) solution would be to give the Class object to the manager's constructor.

ObserverManager<IAction> mgr = new ObserverManager<IAction>(IAction.class);


Yes, it's a simple solution, but I find it bothersome (call me lazy). They could have automated this process to allow for cleaner code (they have allowed for uglier things, such as automagically changing private methods to package-internal when accessed from inner classes, or swallowing entire exceptions when returning in a finally block, etc.)

It looks like generics have been introduced as syntactic sugar, not really for type-checking. Look at Map. It has parameters K and V, for Keys and Values. But why then does the get-method take an Object, not a K? Because a cast wasn't needed. But I still want my type-safety! (had a bug because of that) This is of course only a minor annoyance, but one of many.


Also, generics seem to defy Liskov:
class abstract BaseClass{    public abstract void print(Object obj);}class SubClass<T> extends BaseClass{    public void print(T obj)    {        System.out.println(obj.toString());    }}BaseClass base = new SubClass<String>();base.print( new Integer(5) ); // ...



I hope the C# generics won't be such a letdown.
CaptainJester
CaptainJester
Quote:
Original post by SamLowry
Yes, it's a simple solution, but I find it bothersome (call me lazy). They could have automated this process to allow for cleaner code (they have allowed for uglier things, such as automagically changing private methods to package-internal when accessed from inner classes, or swallowing entire exceptions when returning in a finally block, etc.)


Finally does not swallow exceptions. It just guarantees that it will be executed before any other action is taken. Once you leave the finally block, any unhandled exception continues to be thrown.
"None of us learn in a vacuum; we all stand on the shoulders of giants such as Wirth and Knuth and thousands of others. Lend your shoulders to building the future!" - Michael Abrash[JavaGaming.org][The Java Tutorial][Slick][LWJGL][
SamLowry
SamLowry
As I said, when you return in a finally block, the exception is swallowed.

public String func(){     try     {         throw new NullPointerException();     }     finally     {         return "Hello world";     }}System.out.println( func() );


This code just prints "Hello world", no exception anywhere.

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.