Java Forum / General / January 2006
Question about Generics
Rhino - 18 Jan 2006 17:59 GMT I am currrently trying to "genericize" some of my older classes but I'm having trouble with this simple class:
================================================================ public class IntRange extends TreeSet {
public IntRange(int firstInt, int secondInt) {
add(new Integer(firstInt)); add(new Integer(secondInt)); }
public IntRange(int[] intRange) {
if (intRange.length != 2) { throw new IllegalArgumentException("The IntRange array must have exactly two values."); }
add(new Integer(intRange[0])); add(new Integer(intRange[1])); }
public boolean between(int input) {
if (input >= ((Integer) first()).intValue() && input <= ((Integer) last()).intValue()) return (true); else return (false); }
public int[] getValues() {
int[] values = new int[2]; values[0] = ((Integer) first()).intValue(); values[1] = ((Integer) last()).intValue();
return values; } } ================================================================
Can anyone tell me how to change the add() lines in the constructors to make the compiler accept them? I thought that changing "public class IntRange extends TreeSet" to "public class IntRange <Integer> extends TreeSet" might solve all of my compiler warnings but it didn't; I'm not sure why. Could someone explain what I _should_ do?
Frankly, I found the article on generics in the API pretty unhelpful in getting beyond the most basic ideas. Can anyone point me to an article or tutorial that explains these things more clearly? Then maybe I can "genericize" the rest of my code on my own....
 Signature Rhino
Torkel Franzen - 18 Jan 2006 18:26 GMT > Can anyone tell me how to change the add() lines in the constructors to make > the compiler accept them? I thought that changing "public class IntRange > extends TreeSet" to "public class IntRange <Integer> extends TreeSet" might > solve all of my compiler warnings but it didn't; I'm not sure why. The other way around. Change
public class IntRange extends TreeSet {
to
public class IntRange extends TreeSet<Integer>{
Rhino - 18 Jan 2006 18:43 GMT >> Can anyone tell me how to change the add() lines in the constructors to >> make [quoted text clipped - 10 lines] > > public class IntRange extends TreeSet<Integer>{ Yes, of course, that's it. I figured this out just after I posted but hadn't managed to get back in time to mention that.
But if anyone can still point me to a clearer explanation of generics than the one in the API, I'd love to hear about it :-) I feel sure that other, harder, problems will come to light as I try to make older code use generics.
Rhino
Mike Schilling - 18 Jan 2006 19:17 GMT > But if anyone can still point me to a clearer explanation of generics than > the one in the API, I'd love to hear about it :-) I feel sure that other, > harder, problems will come to light as I try to make older code use > generics. Not the JLS :-)
Seriously, which in previous editions I've gone to the JLS for the definitive answer to thorny questions, JLS 3 is crap. Even many of the examples are wrong.
Torkel Franzen - 18 Jan 2006 20:23 GMT > Seriously, which in previous editions I've gone to the JLS for the > definitive answer to thorny questions, JLS 3 is crap. No, no, it's great stuff.
Mike Schilling - 18 Jan 2006 21:13 GMT >> Seriously, which in previous editions I've gone to the JLS for the >> definitive answer to thorny questions, JLS 3 is crap. > > No, no, it's great stuff. Sorry, I can't tell if you'e joking or being serious. If the latter, can you tell me why you say that?
Torkel Franzen - 18 Jan 2006 21:53 GMT > Sorry, I can't tell if you'e joking or being serious. If the latter, can > you tell me why you say that? Well, it is the ultimate fountain of wisdom, and quite readable. Can you give a couple of examples of thorny questions where it let you down?
Mike Schilling - 18 Jan 2006 23:10 GMT >> Sorry, I can't tell if you'e joking or being serious. If the latter, can >> you tell me why you say that? > > Well, it is the ultimate fountain of wisdom, and quite readable. Can > you give a couple of examples of thorny questions where it let you > down? If my copy were in front of me, I would give chapter and verse. In general, generics, and in particular, many of the examples that were new for JLS 3 do not compile.
Tony Morris - 18 Jan 2006 23:26 GMT > >> Sorry, I can't tell if you'e joking or being serious. If the latter, can > >> you tell me why you say that? [quoted text clipped - 6 lines] > general, generics, and in particular, many of the examples that were new for > JLS 3 do not compile. That's because at one stage they did compile, only the JSR "expert" groups found that they had to change the definition of 'generics' in order to appear less contrived - and without telling anyone (including themselves). Welcome to corporate software.
-- Tony Morris http://tmorris.net/
Java Questions and Answers http://jqa.tmorris.net/
Torkel Franzen - 19 Jan 2006 10:57 GMT > If my copy were in front of me, I would give chapter and verse. In > general, generics, and in particular, many of the examples that were new for > JLS 3 do not compile. I would appreciate information about where these uncompilable examples are to be found.
Mike Schilling - 19 Jan 2006 19:51 GMT >> If my copy were in front of me, I would give chapter and verse. In >> general, generics, and in particular, many of the examples that were new [quoted text clipped - 3 lines] > I would appreciate information about where these uncompilable > examples are to be found. When I locate my copy (it appears to have been "borrowed"), I'll post them.
Torkel Franzen - 18 Jan 2006 20:21 GMT > But if anyone can still point me to a clearer explanation of generics than > the one in the API, I'd love to hear about it :-) I feel sure that other, > harder, problems will come to light as I try to make older code use > generics. You might try Angelika Langer's page:
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html
Rhino - 18 Jan 2006 20:44 GMT >> But if anyone can still point me to a clearer explanation of generics >> than [quoted text clipped - 5 lines] > > http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html Thank you, that looks promising!
Rhino
Thomas Hawtin - 18 Jan 2006 20:25 GMT > public class IntRange extends TreeSet { As mentioned in another reply, that should be:
public class IntRange extends TreeSet<Integer> {
This is a really odd class. It sounds like a range of int (with inclusive values by the looks of the code, which is sensible as you can't have one more than Integer.MAX_VALUE in int).
> public IntRange(int firstInt, int secondInt) { Not very descriptive parameters.
> add(new Integer(firstInt)); > add(new Integer(secondInt)); Can be written as:
add(firstInt); add(secondInt);
If you didn't want to use autoboxing, you can do it slightly better as:
add(Integer.valueOf(firstInt)); add(Integer.valueOf(secondInt));
> public boolean between(int input) { Again you could go for a better method name.
> if (input >= ((Integer) first()).intValue() && input <= ((Integer) > last()).intValue()) > return (true); > else > return (false); This can be better written as:
return first() <= input && input <= last();
> int[] values = new int[2]; > values[0] = ((Integer) first()).intValue(); > values[1] = ((Integer) last()).intValue(); > > return values; Again, that can be written:
return new int[] { first(), last() };
Either of the above could throw a peculiar exception if the set is empty.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Rhino - 18 Jan 2006 20:40 GMT >> public class IntRange extends TreeSet { > [quoted text clipped - 48 lines] > > Either of the above could throw a peculiar exception if the set is empty. That class is trying to capture a range of ints so that there is a way of saying that some third int is or is not within the range depicted by the IntRange. For instance, if I say that the IntRange consists of a low value of 3 and a high value of 9, I want to be able to say that the number 23 is not between the values in the set while 7 is.
I probably haven't done it as well as it could be done; it was really just an early attempt in an old project that I've put to the side for a while. Thanks for your suggestions on improving it; if you have more, please go ahead.
I'm not sure how I could ever have that IntRange be empty to cause me problems: both constructors insist on exactly two values. Is there some way to instantiate the IntRange that I haven't thought of which might cause an empty set?
Rhino
Thomas Hawtin - 18 Jan 2006 22:10 GMT >>public class IntRange extends TreeSet<Integer> { >>> > I'm not sure how I could ever have that IntRange be empty to cause me > problems: both constructors insist on exactly two values. Is there some way > to instantiate the IntRange that I haven't thought of which might cause an > empty set? You could have:
IntRange range = new IntRange(42, 0); range.clear(); System.err.println(range.between(3));
If you want IntRange to support SortedSet<Integer> extend AbstractSet<Integer> and provide the missing methods as needed. A lot of the methods should throw UnsupportedOperationException.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Rhino - 18 Jan 2006 22:34 GMT >>>public class IntRange extends TreeSet<Integer> { >>>> [quoted text clipped - 8 lines] > range.clear(); > System.err.println(range.between(3)); Ahh, I see....
Hmm, maybe I should override the clear() method and make the override empty to prevent that. Or would it be better to make the implementation throw UnsupportedOperationException? Yes, the latter makes more sense, otherwise a user might not realize that clear() is not doing anything by design and that it isn't broken.
> If you want IntRange to support SortedSet<Integer> extend > AbstractSet<Integer> and provide the missing methods as needed. A lot of > the methods should throw UnsupportedOperationException. Yes, I see your point. Thanks for the suggestions!
Rhino
Eric Sosman - 18 Jan 2006 23:19 GMT Rhino wrote On 01/18/06 17:34,:
>>>>public class IntRange extends TreeSet<Integer> { >>>> [quoted text clipped - 16 lines] > user might not realize that clear() is not doing anything by design and that > it isn't broken. I'd take yet another step back and ask why you've decided to make IntRange extend TreeSet. The inherited methods are potential sources of trouble, since the caller can use them to modify the TreeSet without the IntRange's knowledge. And there's nothing in what you've shown us of IntRange that appears to need TreeSet's machinery. Why not just a free-standing IntRange that holds two values?
public class IntRange { private final int low, high; public IntRange(int low, int high) { this.low = Math.min(low, high); this.high = Math.max(low, high); } public IntRange(int[] range) { this(range[0], range[1]); } public boolean isInRange(int value) { return low <= value && value <= high; } public int[] getValues() { int[] values = { low, high }; return values; } }
Unless there's more to IntRange than you've revealed, I'd recommend following the KISS ("Keep It Simple, Stupid!") principle.
 Signature Eric.Sosman@sun.com
Ian Pilcher - 18 Jan 2006 23:32 GMT > Unless there's more to IntRange than you've revealed, I'd > recommend following the KISS ("Keep It Simple, Stupid!") > principle. Presumably, the OP wants his IntRange to behave like a Set<Integer>, with contains, containsAll, iterator, etc.
If this is the case, extending AbstractSet (or possibly AbstractList) is probably the way to go.
 Signature ======================================================================== Ian Pilcher i.pilcher@comcast.net ========================================================================
Rhino - 19 Jan 2006 05:15 GMT >> Unless there's more to IntRange than you've revealed, I'd >> recommend following the KISS ("Keep It Simple, Stupid!") [quoted text clipped - 5 lines] > If this is the case, extending AbstractSet (or possibly AbstractList) is > probably the way to go. In all honesty, I can't remember what reasoning process I was going through when I wrote this IntSet class. That was at least a year ago and I've long since set the project aside, although I want to revive it at some point.
Everyone's comments are much appreciated but I'm not going to be able to act on any improvements right now anyway; maybe in a couple of months if things go well.... In the meantime, your remarks have helped me learn a bit more about making warnings about Generics go away so thanks for that.
Rhino
Stefan Ram - 18 Jan 2006 23:15 GMT >That class is trying to capture a range of ints so that there >is a way of saying that some third int is or is not within the >range depicted by the IntRange. This is somewhat similar to:
http://download.java.net/jdk6/docs/api/javax/swing/DefaultBoundedRangeModel.html
Here is a simple implementation:
class Range { final int min; final int max; public void Range( final int min, final int max ) { this.min = min; this.max = max; } public bool contains( final int value ) { return value >= min && value <= max; }}
I see no reason to use TreeSets, arrays or Integer objects.
Robert M. Gary - 18 Jan 2006 23:06 GMT I will be interested in hearing how this works out for you. I've mostly given up on "Generics" (we call them "Templates" in C++). The problem with Generics is that without a "typedef" type ability you end up with classes who's names can often wrap around your screen. Define an Iterator for one and I've actually had cases where the class name itself was two lines long. C++ solved this with typedef, allowing you to rename the big long ugly class name to something reasonable.
-Robert
Danno - 19 Jan 2006 04:57 GMT I think the idea of generics is that your code should be "generic". In other words, while IntRange is fine for a class, generics force to be more generic in your approach, so I created a regular class called Range. I provided an example below, using your code, that seems to work ok. I included a main method so you can check it out using longs, short, etc.
I am no badass with generics, I just know the regular stuff. You just happened to post while I was researching some advanced uses for generics. ;)
Let me know if that works for ya through here or email. ;)
package com.evolutionnext;
import java.util.TreeSet;
/** * * @author valued customer */ public class Range<E extends Comparable<E>> extends TreeSet<E>{ public Range(E first, E second) { add(first); add(second); }
public Range(E[] range) { if (range.length != 2) { throw new IllegalArgumentException("The Range array must have exactly two values."); } add(range[0]); add(range[1]); }
public boolean input(E element) { return last().compareTo(element) > first().compareTo(element); }
public Object[] getValues() { Object[] result = new Object[2]; result[0] = first(); result[1] = last(); return result; }
public static void main(String[] args) { Range<Integer> range = new Range<Integer>(1, 4); System.out.println(range.input(3)); System.out.println(range.input(5)); System.out.println(range.input(-2)); System.out.println(range.input(0)); System.out.println(range.input(1)); } }
> I am currrently trying to "genericize" some of my older classes but I'm > having trouble with this simple class: [quoted text clipped - 38 lines] > } > ================================================================ Thomas Hawtin - 19 Jan 2006 17:00 GMT > public boolean input(E element) { > return last().compareTo(element) > first().compareTo(element); > } Very clever. But not actually correct.
I think it should go something like:
public boolean contains(E element) { return first().compareTo(element) <= 0 && element.compareTo(last() ) <= 0; }
(Disclaimer: Neither tested nor compiled.)
Not that I think it is a brilliant idea. It appears that the original intention was for a SortedSet of the range. That in general is not going to work for a Comparable. String for instance would iterate over successively long values.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Danno - 19 Jan 2006 17:41 GMT All my test cases passed just fine (Try it for yourself ;) ):
Range<Integer> range = new Range<Integer>(1, 4); System.out.println(range.input(3)); //true System.out.println(range.input(5)); //false System.out.println(range.input(-2)); //false System.out.println(range.input(0)); //false System.out.println(range.input(1)); //true
System.out.println("--------");
Range<Integer> range2 = new Range<Integer>(-5, 5); System.out.println(range2.input(3)); //true System.out.println(range2.input(5)); //true System.out.println(range2.input(-2)); //true System.out.println(range2.input(0)); //true System.out.println(range2.input(1)); //true System.out.println(range2.input(-5)); //true System.out.println(range2.input(-8)); //false
I was assuming from the code that it was inclusive of the boundary. ;)
Thomas Hawtin - 19 Jan 2006 18:46 GMT > All my test cases passed just fine (Try it for yourself ;) ): Just because all of your tests pass, it does not mean your code is correct. Your tests are significantly inadequate.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Danno - 19 Jan 2006 19:17 GMT So test away some more at it. Crash it! It would only help out Rhino in the end. I was just helping someone here on usenet and not getting paid for production quality code. Should I have put a disclaimer to satiate the usenet code critics? Nah, they should test it for themselves.
Other than null handling, which this class doesn't have, I think it is a pretty cool solution. Let me know of any fail case that you find.
Have fun ;) Danno
Thomas Hawtin - 19 Jan 2006 19:57 GMT > So test away some more at it. Crash it! It would only help out Rhino in > the end. Cursory code review caught a problem (and not just that the code was needlessly difficult to understand). I think you would gain more by going back and thinking over the code. There are at least two things I'd probably change in my own version.
> I was just helping someone here on usenet and not getting paid for > production quality code. Should I have put a disclaimer to satiate > the usenet code critics? Nah, they should test it for themselves. You put forward an obfuscated technique that is buggy (surprise!). Are you not expecting anyone to pick up on that? No one should criticise exceptionally poor code?
> Other than null handling, which this class doesn't have, I think it is > a pretty cool solution. Let me know of any fail case that you find. K3wl, certainly. Working, no. Often those things go together.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Danno - 19 Jan 2006 20:14 GMT Show me the buggy! hehe
Plus, it's not exceptionally poor code, I take umbrage at that, I think it kicks a.s for doing it in 10 minutes. If you don't find a bug in that method, you owe me a tortilla. :)
Thomas Hawtin - 19 Jan 2006 21:38 GMT > Show me the buggy! hehe > > Plus, it's not exceptionally poor code, I take umbrage at that, I think > it kicks a.s for doing it in 10 minutes. If you don't find a bug in > that method, you owe me a tortilla. :) You would save time if you wrote more straightforward code.
I expect you to find the bugs. It might persuade you to aim for simpler code.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Danno - 19 Jan 2006 22:01 GMT Whatever I think someone owes me a tortilla
Luc The Perverse - 19 Jan 2006 22:08 GMT > Whatever > I think someone owes me a tortilla Huh why?
-- LTP
:) Danno - 19 Jan 2006 22:31 GMT Because he said there is a bug and I say nay! NAY! I asked him to tell me the bug, and I'd give him a tortilla. He is refusing, because I think he is bluffing. For what it was worth in my few minutes of jotting code late at night on c.l.j.p I thought that little tidbit was weird albeit awesome!
So unless he gives me his expert test case, I am assuming he owes me a tortilla.
John C. Bollinger - 20 Jan 2006 03:17 GMT >> public boolean input(E element) { >> return last().compareTo(element) > first().compareTo(element); >> } > > Very clever. But not actually correct. You at least have to award him partial credit. I see one class of error cases where the above method provides results inconsistent with Rhino's between() method, but otherwise it does look like it works.
> I think it should go something like: > [quoted text clipped - 5 lines] > > (Disclaimer: Neither tested nor compiled.) That one solves the problem I see with Danno's. Sorry, Danno, no tortilla for you.
> Not that I think it is a brilliant idea. It appears that the original > intention was for a SortedSet of the range. That in general is not going > to work for a Comparable. String for instance would iterate over > successively long values. No, I think the intention was to hijack TreeSet's machinery to evaluate properties and conditions of the range, such as the one you and Danno are arguing over. The set itself is not intended to be an exhaustive collection of the elements of the range, and might never contain more than the two endpoints.
You're right, it's not a brilliant idea, but not for the reason you mentioned. I think Rhino talked about this back when he first was working on it; I didn't like it then, either.
-- John Bollinger jobollin@indiana.edu
Danno - 20 Jan 2006 04:18 GMT Now if we are talking about taking it from the top and organizing a way to do this correctly. I wholeheartedly agree. I wouldn't approach it this way at all. All I did was take Rhino's code for better or worse and converted it. ;)
I sure like tortillas though.
Oliver Wong - 20 Jan 2006 21:39 GMT >> public boolean input(E element) { >> return last().compareTo(element) > first().compareTo(element); [quoted text clipped - 11 lines] > > (Disclaimer: Neither tested nor compiled.) For what it's worth, I could not find an example where the two methods would return different values given the same input, except if element is null, in which case Thomas' method would throw an NPE, and Danno's method would behave depending on how compareTo() is implemented.
This is assuming that the contract for compareTo() is properly obeyed, and that first() and last() always return the same values, and that that value is not null.
- Oliver
Eric Sosman - 20 Jan 2006 22:46 GMT Oliver Wong wrote On 01/20/06 16:39,:
>>> public boolean input(E element) { >>> return last().compareTo(element) > first().compareTo(element); [quoted text clipped - 20 lines] > and that first() and last() always return the same values, and that that > value is not null. For what it's worth, two scenarios:
1: If the range is trivial (first() and last() compare equal) and the element being tested is that same value, Danno's method returns false while Oliver's returns true.
2: compareTo() returns a value whose sign is important but whose magnitude is not. If last().compareTo(element) returns 7 and first().compareTo(element) returns 3, Danno's method returns true while Oliver's returns false.
 Signature Eric.Sosman@sun.com
Thomas Hawtin - 21 Jan 2006 00:01 GMT > For what it's worth, two scenarios: > [quoted text clipped - 6 lines] > returns 7 and first().compareTo(element) returns 3, Danno's > method returns true while Oliver's returns false. I think Danno owes Eric a tortilla on both counts.
Tom Hawtin
 Signature Unemployed English Java programmer http://jroller.com/page/tackline/
Danno - 21 Jan 2006 22:03 GMT Damn! Oh well, you win some, you lose some.
If you are interested in some tortillas, email me with an address so I can send them to you. I could also pitch in some chile from NM too. ;)
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|