changed:
-
<hr><b>2004/09/19 18:35 EST (via web):</b><br>
Remember to check the "with heading" box in the lower right corner.
Surround code listings with <pre> and </pre>
On window for "grep" there is one open source tool
http://www.powergrep.com/
try it out
u don't have to switch to linux for srach :)
[[ This is 99$, not open source. But see below re: findstr, already on Windows but I didn't know it. -- Bruce ]]
<hr><b>2004/09/20 04:28 EST (via web):</b><br>
Okay, so erasure creates really ugly warnings in generic classes. What else?
I was expecting to see more examples of code impact.
Are there any other common Java idioms that are effected?
The article leaves me wanting more. [[ I plan to write more. I've just been thrashing
around in research mode for so long that I had to start somewhere -- Bruce ]]
The fact that Java allows casting between from T[] to Object[] and vice versa
is a hole in the type system. Until now this was rarely done, but now generics REQUIRE you to do it? Crazy!
You can't cast from T[] to Object[] without generics.
Why to do this?
<pre>
private T[] array;
...
array = (T[])new Object[sz];
</pre>
When you can do:
<pre>
private Collection<T> array=null;
...
array=new ArrayList<T>();
</pre>
<I>If you observe that you are using Windows which has no GNU grep, you're in luck – there's a terrific project called cygwin...</I>
If you observe that Windows has the "findstr" command, you don't need cygwin.
[[ Thanks. I didn't realize that findstr had been added (when?). That's good to know.-- Bruce ]]
- Dan
<hr><b>2004/09/20 11:15 EST (via web):</b><br>
Back to the future!?
It's really overblown to install Linux on Windows just to do a text search... and grep is quite nasty to use.
My favourite text search tool is searchrapid, www.searchrapid.com, not freeware (except for noncommercial use)
but cheap and really useful. [[ As Dan pointed out, findstr is already part of Windows and does regular expressions.
I actually use cygwin to solve many other problems, and it's a straighforward installation. But if you don't
know Unix then it's probably best to use findstr. -- bruce ]]
<hr><b>2004/09/20 11:17 EST (via web):</b><br>
Excuse, * www.inforapid.com * (and I'm not the author).
<hr><b>2004/09/20 19:33 EST (via web):</b><br>
I think you are still thinking in C++ [sorry - pun], the code you give in Java Generics should declare the array to be of type Object[]. This is because T in your example doesn't specify any constraint (i.e. there is no extends <Type> after the declaration of T). Therefore your T's are really Objects so declare them as Objects including arrays and don't cast to T's - there is no need. You are making this much more complicated than you need to.
The example you give from Collections is misleading because it needs to return an array, it is the bridge method between the old arrays and the new collections. The idea in 5 is to use collections, i.e. instead of using T[] use List<T>. The conversion to T[] is for backward compatability with old API's.
In summary you still need the ugly cast for backward compatability but not for your own code and not for new APIs.
<hr><b>2004/09/20 19:38 EST (via web):</b><br>
In the above 2nd last para., 2nd last sentance it says "instead of using T[] use List" it should say "instead of using T[] use List LESS_THAN T GREATER_THAN" but the Wiki removed the angle braces and the "T", presumably it thought it was HTML. Sorry.
The two comments above are wrong. Bruce is right to use T[] and not Object[]. If you used Object, you would need to cast from Object to T *each time you access the generic array*.
As for using List, obviously it only hides the problem, since the cast is in ArrayList. Unless you take the stance that you should never use simple arrays at all. Personally I think arrays have their uses. In particular, when the length is fixed, it's better to use an array than a List, since a list provides you an (optional) operation "add", which should not be present in that case.
<hr><b>2004/09/21 05:40 EST (via web):</b><br>
Bruce, you are right, this is a serious limitation in Java 5. But it is perfectly possible to do better while compiling to an unchanged JVM. For instance, here is the same code in Nice (http://nice.sf.net), without any cast:
<pre>
public class GenericArray<T> {
private T[] array;
public void set(int index, T item) {
array[index] = item;
}
public T get(int index) {
return array[index];
}
}
// Constructor
public <T> new GenericArray(int sz) {
this(array: new T[sz]);
}
void main(String[] args) {
GenericArray<int> gai = new GenericArray(10);
for (int i = 0; i < 10; i ++)
gai[i] = i;
for (int i = 0; i < 10; i ++)
System.out.println(gai[i]);
}
</pre>
Note also that the code is using [.] notation to call the get and set methods.
[[For some reason it made me quite cheerful to see the Nice language and its sensible
and powerful syntax. This means we could move forward in the language without losing
the libraries, arguably Java's greatest strength. I will have to look some more at
Nice. -- Bruce ]]
<hr><b>2004/09/21 06:00 EST (via web):</b><br>
There are two posts above suggesting that changing the T array to simply be an Object array won't work, one below a correction to the original suggestion (without a header line) and one for Nice. Changing to an Object array does work I tried it on a 5 compiler and Bruce's example works fine, you do however need to add a cast to the get method. However all the attribute stuff and compiler flags are unecessary in Bruce's example.
Also the post above without the header line suggests that you can't use Lists instead of arrays, you can and that is the intention with generics. One difference is that an array list or a linked list is not fixed size, however if you do need a fixed sized list use Arrays.toList on an array. The only point of arrays is that they have nicer syntax, I think in all other circumstances lists with generics will be superior.
Reply: that's what I meant: if you use Object, you need a cast for the get method, which is a bad thing. About lists, I'm not saying you cannot use it, I'm saying you should also be able to use arrays without casts. Using Arrays.toList is fine, but you get no guarantee that add will not be called, only that an exception will be thrown at runtime if it is, which is not satisfactory either.
<pre>
package com.foo;
/**
* TODO
*/
public class GenericArray <T> {
public interface ArrayCreator <T> {
public T[] make( int size );
}
private T[] array;
private ArrayCreator<T> creator;
public GenericArray( int sz, ArrayCreator<T> aCreator ) {
creator = aCreator;
array = creator.make( sz );
}
public void put( int index, T item ) {
array[index] = item;
}
public T get( int index ) {
return array[index];
}
public static void main( String[] args ) {
ArrayCreator<Integer> creator = new ArrayCreator<Integer>() {
public Integer[] make( int size ) {
return new Integer[size];
}
};
GenericArray<Integer> gai = new GenericArray<Integer>( 10, creator );
for ( int i = 0; i < 10; i++ ) {
gai.put( i, i );
}
for ( int i = 0; i < 10; i++ ) {
System.out.println( gai.get( i ) );
}
}
}
</pre>
<hr><b>2004/09/21 20:50 EST (via web):</b><br>
Reply to the above: Sorry if I mis-understood your post. You raise two points firstly how to avoid casts and secondly using Lists instead of arrays.
Avoiding casts:
I agree that your solution will also work without all the compiler flags etc. that Bruce proposed. There are also variations on this theme where GenericArray is made abstract and make is an abstract method in GenericArray. Then you subclass it for each type you want, e.g. class IntegerArray extends GenericArray< Integer > { ... }.
However I still prefer the first solution that I proposed of using an Object array and a cast in get. The advantage of this approach is that all the GenericArrays share the same code and this reduces code bloat. Note: to the user of the class the approach I suggested is particularly easy they simply say GenericArray< Integer > to declare an array.
Using Lists Instead of Arrays:
If you really want a fixed length GenericArray then it is trivial to write, about 10 lines (using the Object array and the cast in get). I prefer to use a List because it is more flexible, different types like: linked lists, unmodifiable lists, etc. Although the idea of methods that may throw a runtime exception does weaken type checking I think it is worthwhile for the extra flexibility. The decision to have methods that throw runtime exceptions is probably still counted as contriversial but I think that overall the design of the Collections library is excellent and I think that Joshua Bloch deserves considerbale credit.
Summary:
The main point is that there are simple solutions to the GenericArray problem that don't require a
nnotations and compiler flags and these solutions have advantages compared to arrays and that with
J5 we should all start using List< T > instead of T[]. Think of arrays as depreciated!
[[ Arrays can't be deprecated. They are, for example, what ArrayList is built from. At some point you have
to have arrays as a fundamental sequence type. I think you mean to say that we should prefer the java.util.Collection
family to using arrays, and certainly I agree with that. But sometimes you have to create your own collections,
and sometimes you need to actually use arrays. And in this article and those to follow I am exploring the
'edges' of generics, which I think many people will bump up against at some point. Also, to be an effective
programmer I think you need to know that the edges are there, and what they do. This isn't just to see what
you can't do, but also what you can do. -- Bruce ]]
<hr><b>2004/09/21 21:52 EST (via web):</b><br>
This is the code for Bruce's example using an <code>Object</code> array and a cast in the <code>get</code> method to show how simple it is:
<pre>
package junk.generics.brucearrayexample;
import static java.lang.System.*;
public class Array<T> {
private Object[] array;
public Array(int sz) {
array = new Object[sz];
}
public void put(int index, T item) {
array[index] = item;
}
public T get(int index) {
return (T)array[index];
}
public static void main(String[] args) {
Array<Integer> gai = new Array<Integer>(10);
for(int i = 0; i < 10; i ++)
gai.put(i, i);
for(int i = 0; i < 10; i ++)
out.println(gai.get(i));
}
}
</pre>
[[ I don't get the difference. You've simply moved the unchecked cast from the
array definition to the get() statement. But you still have an unchecked cast,
so it doesn't seem particularly different to me. What were you trying to say? -- Bruce ]]
</pre>
<hr><b>2004/09/21 23:39 EST (via web):</b><br>
Hi Bruce, I have to eat humble pie. There is no difference, I had -nowarn set on my compiler in the IDE so when I compiled the above it didn't produce the warning. When I take -nowarn off it behaves like you said. Sorry for the inconvenience - Howard.
Just a side note: Eclipse offers a similar search facility, including support for file name patterns and regular expressions. The results are displayed in a package tree, the files can be opened from there to go straight to the relevant bit of source. I find Eclipse an incredible useful tool to browse code in general -- I actually nearly stopped reading JavaDocs, I just browse the code.
Enough evangelism -- I'm not paid for it :-)
-- Peter
On grep and cygwin: You need not have full cygwin installed, only grep.exe and cygwin1.dll
<hr><b>2004/09/23 14:21 EST (via web):</b><br>
Please see http://www.gafter.com/~neal/Erasure.html
-Neal Gafter
Hi Bruce,
Nice bit... It really starts to demonstrate on why I am beginning to believe that the JDK 5.0 should not make the light of day. I mean this is crazy... we need an annotation to tell the compiler to not do something that it normally does? This is truly a code smell! You layers of tweaks upon hacks to get something to work nicely and the more you add, the more complexity you add, the more likely it will be to break and the more likely that someone will get it wrong.
I've have been programming in Java for 8 years. Prior to that I was coding in Smalltalk. What I have found is that the less abstraction that you allow, the less useful the language and the more effort it takes to solve a problem. Generics decrease the level of abstraction with all of these costs and now we are going to add in annotation??? I'm sorry, it doesn't make sense to me and this is a direction that saddens me.
Lets start over... what is the real problem that we are trying to solve with generics and can't we bury this in the VM/compiler rather then throwing it onto the shoulders of the developers?
- Kirk Pepperdine
Java Performance Tuning.com
http://unxutils.sf.net has native Win32 ports of many GNU utilities (including grep).
Sorry about the above, I have no clue how to use a wiki properly. -- Andrew