Generics in production code?

Hi all!

As you probably noticed, we - after much debate - plan to support JSR014
"Generics" in Aurora. And you can expect at least the parser in a build or
two :).

However, our motto here is "to eat one's dog food" - that is, we would like
to use Generics in IDEA (and, as a matter of fact, are looking forward to
do so :) ).

The current beta implementation of Generics (version 1.3) can be used "on
top" of JDK 1.4.1 - that is, compile with generics supporting javac, and
run with JDK 1.4.1. You can even enjoy generic collections - a special
generic stubs are provided, against which your code should be compiled. And
we were actually able to compile IDEA with it and use it afterwards :)
(no, the next build is compiled with conventional javac of 1.4.1_something).

However, latest statements from Sun are somewhat vague on the point of
whether this happy state of events will continue past a revised version of
JSR014, due in the end of May. That is, it may well be that code using
Generics will not run on older JVMs.

So, the question is - have you tried to use Generics in production code?
Do you consider doing so before 1.5 is out? If you do so, how are you going
to cope with changes from Sun?

Your comments are very welcome.

Cheers,
Dmitry

--
Dmitry Lomov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

64 comments
Comment actions Permalink

What exactly is so exicting about Generics ?

>

You can have type-safe collections (which is not bad) and you save a
couple of type casts (syntactic sugar).
I wouldn't call that a killer feature ;)


Generics provide more compile-time safety, that reduce the risk of
ClassCastExceptions at runtime.

It would be far more interesting to have primitive types in collections,
for example an IntHashMap, or a LongTreeSet, etc.


I almost never need to put primitives in arrays or collections. Do you?

Tom

0
Comment actions Permalink

"Thomas Singer" <thomas.singer@noregnisspam.de> wrote:

You can even enjoy generic collections - a special generic stubs are
provided, against which your code should be compiled.

>

Are they backward-compatible, so we can switch to Generics step-by-step?


I might be wrong here, but I think no, they are not backward compatible.

When compiling against the generic enabled collection stubs you have to provide
the template parameter(s). That means that
List list = new List();
is no longer valid. To get the original List-behavior, you have to write
List<![CDATA[ list = new List]]>();

Unfortunately, this makes step-by-step migration a bit hard. One possibility
could be to have several separate sub-projects (aka modules) and use the
collection stubs only for selected ones.

Any other ideas?

Sascha


0
Comment actions Permalink

I might be wrong here, but I think no, they are not backward compatible.

>

When compiling against the generic enabled collection stubs you have to
provide
the template parameter(s). That means that
List list = new List();
is no longer valid. To get the original List-behavior, you have to write
List<Object> list = new List<Object>();


If this is the case, is it possible to have the stubs using a alternative
package instead of java.util?

Tom

0
Comment actions Permalink

"Thomas Singer" <thomas.singer@noregnisspam.de> wrote:

I might be wrong here, but I think no, they are not backward compatible.

>

When compiling against the generic enabled collection stubs you have to
provide
the template parameter(s). That means that
List list = new List();
is no longer valid. To get the original List-behavior, you have to write
List<Object> list = new List<Object>();

>

If this is the case, is it possible to have the stubs using a alternative
package instead of java.util?


Negative. The stubs just trick the compiler but the compiled code runs against
the usual Collection classes (you don't even need the stubs at runtime). Even if
it was possible to re-package the stubs, the compiled code wouldn't run then.

What might be possible is to have an own Generics-enabled Collection framework
in a seperate package that delegates to the original Collections. But I'm not sure if
that would work well.

Sascha


0
Comment actions Permalink

Sascha Weinreuter wrote:

"Thomas Singer" <thomas.singer@noregnisspam.de> wrote:

>> > You can even enjoy generic collections - a special generic stubs are
>> > provided, against which your code should be compiled.
>>
>> Are they backward-compatible, so we can switch to Generics step-by-step?


I might be wrong here, but I think no, they are not backward compatible.


I am afraid you are wrong here (which is very good news actually) :)


When compiling against the generic enabled collection stubs you have to
provide the template parameter(s). That means that
List list = new List();
is no longer valid. To get the original List-behavior, you have to write
List<Object> list = new List<Object>();


The first variant is still valid (it is called a raw type), and
collections are backward compatible, so one actually can switch to generics
step by step ;). See draft spec for more info.

BTW, List is not equivalent to List<![CDATA[]]>.

As I said in my posting we were actually able to compile the whole IDEA with
generics-enabled javac against generic collections.


Cheers,
Dmitry
--
Dmitry Lomov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

"Dmitry Lomov" <dsl@intellij.com> wrote:

>> Are they backward-compatible, so we can switch to Generics step-by-step?
>

I might be wrong here, but I think no, they are not backward compatible.

>

I am afraid you are wrong here (which is very good news actually) :)


Cool. I didn't know that. You are right, this is great news :)

When compiling against the generic enabled collection stubs you have to
provide the template parameter(s). That means that
List list = new List();
is no longer valid. To get the original List-behavior, you have to write
List<Object> list = new List<Object>();

>

The first variant is still valid (it is called a raw type), and
collections are backward compatible, so one actually can switch to generics
step by step ;). See draft spec for more info.


Hm, looks like I should read more carefully :)

Thanks for clearing that up Dmitry.

Sascha



0
Comment actions Permalink

We could not use it because IDEA does not support it
yet. I would not trade IDEA even for generics.
If IDEA supports generics I will give it a try on
some less important projects/parts of projects
where I can mitigate the risk of incompatibility


Same here. Also, generics are really most useful during development, so if there were a tool to strip the type parameters (or what ever they are called) either from the source code or the resulting classes, then one could use generics while developing, thus test the IDEA support for them, and then ship a non-generic version. That's essentially what the erasure mechanism that they were planning to use when I last heard would have done anyway.

Then there are the systems that are beginning to be developed now, but which aren't expected to ship until 1.5 will be out...

0
Comment actions Permalink

Yes, fortunatelly you ARE wrong. The old syntax is still valid. Not only that, but the generics-aware collections can still interact with non generic-aware code. That way, the following statement is still valid;

List list = new ArrayList();
list.add(new AnythingYouWant();

And the following is also possible:

List<![CDATA[ list = new ArrayList(); List otherList = list; That way, what is returned from methods that returns a parameterized collection can safely be assigned to a "raw" old style collection. Better yet, the following also yields true: List stringList = new ArrayList(); List integerList = new ArrayList]]>();

return stringList.getClass() == integerList.getClass();

So I think generics adoption can happens at the pace you want. You can slowly change parts of your program while the rest of it will happily accepts what you changed.

By the way, I'm not just guessing: the above statements are based on the JSR-14 public review specification document. Everything I said above is stated in the specification as well.

0
Comment actions Permalink

I could see a need for some Generics based intentions as well, i.e. notice
that you're creating a List foo = new List(); list.add( new Integer());
and suggest changing it to List<Integer>. Would have to be quite smart
thou...


We plan to implement another feature - something like "Convert to use of
Generics". It will run on your project (or only selected package/file) and
try to convert it from using non-parametrized collections into parametrized
ones. Would you find it useful?

--
Valentin Kipiatkov
JetBrains, Inc
http://www.intellij.com
"Develop with pleasure!"

"Mark Derricutt" <pinhead@satinism.org> wrote in message
news:pan.2003.05.13.06.40.21.867202@satinism.org...

On Mon, 12 May 2003 22:39:13 +0400, Dmitry Lomov wrote:

>

Generics affect many things - code insight, refactorings, intentions,
generation (Alt+Ins), to name a few - and we really need the community
opinion and ideas on how to integrate generics nicely into all that...

>

I could see a need for some Generics based intentions as well, i.e. notice
that you're creating a List foo = new List(); list.add( new Integer());
and suggest changing it to List<Integer>. Would have to be quite smart
thou...

>
>

--
...turn to the light - don't be frightened by the shadows it creates,
...turn to the light - turning away could be a terrible mistake
...dream theater - the great debate

>


0
Comment actions Permalink

Valentin Kipiatkov wrote:
>>I could see a need for some Generics based intentions as well, i.e. notice
>>that you're creating a List foo = new List(); list.add( new Integer());
>>and suggest changing it to List<![CDATA[. Would have to be quite smart >>]]>thou...


We plan to implement another feature - something like "Convert to use of
Generics". It will run on your project (or only selected package/file) and
try to convert it from using non-parametrized collections into parametrized
ones. Would you find it useful?


Sounds interesting, a sort of automatic generics refactoring. Aren't you
guys going to let us do any repetitive/tedious programming anymore? ;)

Jon

0
Comment actions Permalink


Ok, ok I got it. I don't really remember how I got on this trip.
I did read the specification, but I somehow missed or forgot about the raw types,
and came to a wrong conclusion.

Sorry for spreading wrong rumors. :(

Sascha

"Marcus Brito" <pazu@animegaiden.com.br> wrote:

Yes, fortunatelly you ARE wrong. The old syntax is still valid. Not only that, but the

generics-aware collections can still interact with non generic-aware code. That way, the following
statement is still valid;
>

List list = new ArrayList();
list.add(new AnythingYouWant();

>

And the following is also possible:

>

List<String> list = new ArrayList<String>();
List otherList = list;

>

That way, what is returned from methods that returns a parameterized collection can safely be

assigned to a "raw" old style collection.
>

Better yet, the following also yields true:

>

List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();

>

return stringList.getClass() == integerList.getClass();

>

So I think generics adoption can happens at the pace you want. You can slowly change parts of your

program while the rest of it will happily accepts what you changed.
>

By the way, I'm not just guessing: the above statements are based on the JSR-14 public review

specification document. Everything I said above is stated in the specification as well.


0
Comment actions Permalink

I almost never need to put primitives in arrays or collections. Do you?


I definitely do. But, the new autoboxing feature in 1.5 should make this
much easier.

--
Jordan Zimmerman
Altura International
Catalog City


0
Comment actions Permalink

That would be incredible. +10

--
Jordan Zimmerman
Altura International
Catalog City


0
Comment actions Permalink

Would love to, however, in production, we are still using 1.3.1. We might get to 1.4 by the second half of this year, but I am not counting on it; trust me it causes problems.

The code I write runs in Websphere(3.5), on z/OS, i.e., the mainframe world. There, they are very cautious about upgrades(version or otherwise), until they can prove it is stable in test.

The up side is that we(the code writers) don't get upset customers because of a bad upgrade(even though we didn't do it, the customer still thinks we are responsible...and they are partly right).

The down side is that we have to deal with the hassles, problems, bugs, and such of the older releases.

I doubt we will see 1.5 until late 2004, probably 2005.

Given that stability is a primary concern(speed is not of much use if the application is not working), I could not risk using generics(though I would love to); I am not sure they will even work in 1.3.

Don't know if that helps you any.

However, one thing is important, which I am sure you have already considered/covered, is to make sure that the use of generics(and everything that goes with it) is either optinal and/or non-obtrusive to those of us who still have to work in older versions of JAVA.

0
Comment actions Permalink

that's what i'd use.

0
Comment actions Permalink

I almost never need to put primitives in arrays or

collections. Do you?

I definitely do. But, the new autoboxing feature in
1.5 should make this
much easier.


Auto-Boxing will make the source code cleaner, but the resulting byte code is still using objects.

0
Comment actions Permalink

"Valentin Kipiatkov" wrote:

We plan to implement another feature - something like "Convert to use of
Generics". It will run on your project (or only selected package/file) and
try to convert it from using non-parametrized collections into parametrized
ones. Would you find it useful?


Yes, this would be very cool. I'd vote for this.

Paul

0
Comment actions Permalink

Very sweet. Just the sort of thing I would hope for from IntelliJ/JetBrains.

0
Comment actions Permalink

That would be great!

It would also be useful to have the option to do the opposite: convert generics based code to jdk1.[34] code.

That would make it safe to check out generics - just switch back if you don't like it, or if Sun decides to postpone jdk1.5 until 2007 :)

0
Comment actions Permalink

On Thu, 15 May 2003 01:39:14 +0000, Dan Holmsand wrote:

That would be great!

It would also be useful to have the option to do the opposite: convert
generics based code to jdk1.[34] code.

I was going to suggest that as well.
This feature would be so great!

This is the kind of difference between other IDEs and IDEA!

0
Comment actions Permalink

Marcus Brito <pazu@animegaiden.com.br> wrote:

List<String> list = new ArrayList<String>();
List otherList = list;


and what happens if you do a otherList.add(new Integer(42))?

Dirk Dittert

0
Comment actions Permalink

List<String> list = new ArrayList<String>();
List otherList = list;


and what happens if you do a otherList.add(new
Integer(42))?


I would expect this to compile but throw a RuntimeException, wouldn't you?

Regards,
Jens

--
Before you criticize someone, you should walk a mile in his shoes.
That way, when you criticize him, you are a mile away from him and you have his shoes.

0
Comment actions Permalink

I would expect this to compile but throw a RuntimeException, wouldn't
you?


I guess, there will be an ClassCastException when fetching Strings out of
the list, not at insertion point.

Tom

0
Comment actions Permalink

I would expect this to compile but throw a

RuntimeException, wouldn't

you?


I guess, there will be an ClassCastException when
fetching Strings out of
the list, not at insertion point.


That would surprise me. The datatype of a typed collection (the so-called Signature) is a property of that collection, and it should be enforced at all times.

Regards,
Jens

0
Comment actions Permalink

Thomas Singer wrote:

>> I would expect this to compile but throw a RuntimeException, wouldn't
>> you?


I guess, there will be an ClassCastException when fetching Strings out
of the list, not at insertion point.


Yes, this is correct. See Draft Spec for more details and answers to whys.

Friendly,
Dmitry


--
Dmitry Lomov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

Valentin Kipiatkov wrote:

>> I could see a need for some Generics based intentions as well, i.e.
>> notice that you're creating a List foo = new List(); list.add( new
>> Integer());
>> and suggest changing it to List<![CDATA[. Would have to be quite smart >>]]> thou...


We plan to implement another feature - something like "Convert to use of
Generics". It will run on your project (or only selected package/file) and
try to convert it from using non-parametrized collections into
parametrized ones. Would you find it useful?


Well, after some discussion we have found out that it will require some
really non-trivial analysis to obtain non-trivial results. Consider

<![CDATA[
public interface UserSearchHelper {  
  List createUserList();
  User getUser(List userList, String name);
}

public class SecurityManager {
    UserSearchHelper myHelper = getUserSearchHelper();
    private List myUsersList = myHelper.createUserList();

    void doSomethingUserful() { // pun intended :)
        ...
        myUserList.add(new User("john"));
        myUserList.add(new User("vasya"));
        ...
        User user =        
           myHelper.getUser(myUsersList,System.getProperty("user.name"));
        ...
    }
    
}
]]>


It is quite obvious to human that myUsersList is actually List<![CDATA[]]>, but
not at all trivial (impossible?) for a tool...
So do not expect too much of a magic :)

Friendly,
Dmitry

--
Dmitry Lomov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"

0
Comment actions Permalink

It would also be useful to have the option to do the opposite: convert

generics based code to jdk1.[34] code.

Ok, that's even easier to implement. Will do it as well.

--
Valentin Kipiatkov
JetBrains, Inc
http://www.intellij.com
"Develop with pleasure!"


"Dan Holmsand" <dan@eyebee.com> wrote in message
news:24007705.1052962754602.JavaMail.jrun@is.intellij.net...

That would be great!

>

It would also be useful to have the option to do the opposite: convert

generics based code to jdk1.[34] code.
>

That would make it safe to check out generics - just switch back if you

don't like it, or if Sun decides to postpone jdk1.5 until 2007 :)


0
Comment actions Permalink

Well IMO there is no problems in that case provided that ALL implementations
of UserSearchHelper are available as well.
If not (for example, it's to be extended by third parties) that might be a
problem.

--
Valentin Kipiatkov
JetBrains, Inc
http://www.intellij.com
"Develop with pleasure!"

"Dmitry Lomov" <dsl@intellij.com> wrote in message
news:b9vucl$h8f$1@is.intellij.net...

Valentin Kipiatkov wrote:

>

>> I could see a need for some Generics based intentions as well, i.e.
>> notice that you're creating a List foo = new List(); list.add( new
>> Integer());
>> and suggest changing it to List<Integer>. Would have to be quite

smart

>> thou...
>

We plan to implement another feature - something like "Convert to use of
Generics". It will run on your project (or only selected package/file)

and

try to convert it from using non-parametrized collections into
parametrized ones. Would you find it useful?

>

>

Well, after some discussion we have found out that it will require some
really non-trivial analysis to obtain non-trivial results. Consider

>

<![CDATA[
> public interface UserSearchHelper {
>   List createUserList();
>   User getUser(List userList, String name);
> }
>
> public class SecurityManager {
>     UserSearchHelper myHelper = getUserSearchHelper();
>     private List myUsersList = myHelper.createUserList();
>
>     void doSomethingUserful() { // pun intended :)
>         ...
>         myUserList.add(new User("john"));
>         myUserList.add(new User("vasya"));
>         ...
>         User user =
>            myHelper.getUser(myUsersList,System.getProperty("user.name"));
>         ...
>     }
>
> }
> ]]>

>

It is quite obvious to human that myUsersList is actually List<User>, but
not at all trivial (impossible?) for a tool...
So do not expect too much of a magic :)

>

Friendly,
Dmitry

>

--
Dmitry Lomov
IntelliJ Labs / JetBrains Inc.
http://www.intellij.com
"Develop with pleasure!"



0
Comment actions Permalink

Ok, that's even easier to implement. Will do it as well.


Great!

/dan

0
Comment actions Permalink

Dmitry Lomov wrote:

It is quite obvious to human that myUsersList is actually List<User>, but
not at all trivial (impossible?) for a tool...
So do not expect too much of a magic :)


I think I would be quite satisfied with some context-dependent actions
rather than a global analysis. I might even prefer this since it would
give me full control over each step in the entire process rather than
having hundreds of changes made automatically.

Maybe something like this (just thinking out loud):

1) A way of finding all the places where you use raw types instead of
the real generic versions. Maybe a code inspection option, or maybe a
yellow warning underline so that you could easily skip from one place to
the next.

By the way, this is something I really miss in IDEA. I'd like to be
able to step through all warnings and errors globally, not just within
a single file. It doesn't matter that it takes time to analyze each
file -- it takes much more time for me to manually load each file to
check for warnings, and using the code inspection tool instead is much
less convenient because it is not incremental. Something like F2 which
just keeps stepping forward, skipping to the next file when you reach
the end of one file, would be great.

2) If you declare a local variable "List foo = new ArrayList();" there
should be an action letting you convert this to a generic type. This
could convert the line to List<![CDATA[<...> foo = new ArrayList<...>();" where you can type into "..." just like you can in live templates. For the standard collection types, this action would look at all places where the local variable is used and try to infer the intended type parameters, and fill the autocompletion lists with that type and maybe its supertypes/interfaces. If you can't get a good guess, just suggest Object. It should be possible to determine from the interface definition which generic parameters (T1,T2 in Map) end up in various argument positions in methods or as return value types, although if this seems to be overkill it would be possible to just hardcode a few method calls to be used in the analysis of the standard collection interfaces. 3) Similarly for fields, although you could analyze the usage of the field throughout the file or even throughout the entire project. Just direct usages of the field, not indirect ones. 4) Similarly for method parameters. Then we could turn on warnings for raw types, press the "super-F2" function to step to the next warning, change foo to the intended type (List), which generates a warning for bar(foo) since bar takes a raw list but you're sending in a List so you can accept the intention to change the parameter type to List and then accept the inferred type String (and accept IDEA's suggestion to propagate that throughout the inheritance hierarchy). Then press super-F2 again, end up at a (String) cast that is no longer necessary, alt-enter enter to remove it, and so on. It takes a bit of work, but I would probably be much more confident that the results were correct compared to running a global analysis which might just default to MyType in too many places. If this turns out to require too much manual work, maybe some kind of type change propagation would help. That is, if you strengthen the requirements on a method parameter by changing it from List to List, there could be an option to automatically propagate that to all callers, so that the actual parameters would be forced to be of type List, which might in turn add constraints on the return value of a method (which was used as an actual parameter), or on the type of a local variable, and so on. This could be useful for other tasks too, not just when you add generics to a program. For example: ... List plainList = new ArrayList(); myMethod(plainList, getMap()); ... void myMethod(List listarg, Map maparg) { ... } Map getMap() { ... } Now if you change the type of listarg to List and maparg to Map, then you can look at all invocations, for example myMethod(plainList), and see that plainList must be assignable to List, so you can strengthen the type to List: ... List plainList = new ArrayList(); myMethod(plainList, getMap()); ... void myMethod(List listarg, Map maparg) { ... } Map getMap() { ... } And then you could see that getMap() must return a map of a specific type and that plainList must be initialized with some type of List: ... List plainList = new ArrayList(); myMethod(plainList, getMap()); ... void myMethod(List listarg, Map maparg) { ... } Map]]> getMap() { ... }

As I said, I'm just thinking out loud. You might already have much
better ideas...


0

Please sign in to leave a comment.