Parameterized types
This program:import java.util.*;
publicclass ListenerDriver
{
publicstaticvoid main(String[] args)
{
ListenedList<String> list =new ListenedList<String>();
list.addMyListener(new MyListener());
list.addElement(new String("Life is a state of mind"));
list.addElement(new String("Why a duck?"));
for (int x = 0 ; x < list.size() ; x++)
System.out.println(list.get(x));
}
}
class ListenedList<T>extends ArrayList
{
MyListener myListener =new MyListener();
publicvoid addMyListener(MyListener myListener )
{
this.myListener = myListener;
}
publicvoid addElement(String element)
{
add(element);
myListener.elementAdded(element);
}
}
class MyListener
{
publicvoid elementAdded(String element)
{
System.out.println("\"" + element +"\"" +" was added to the list.");
}
}
runs OK but the compiler gives the following warning:
Type safety: The method add(Object) belongs to the raw type ArrayList.
References to generic type ArrayList<E> should be parameterized
I think I understand the message but I can't seem to correct it.
?
Thanx to one and all.
You're extending the raw ArrayList type instead of the parameterized version:
class ListenedList<T> extends ArrayList
Should be:
class ListenedList<T> extends ArrayList<T>
Yes, I tried that.That won't even compile:The method add(T) in the type ArrayList<T> is not applicable for the arguments (String)
> Yes, I tried that.
> That won't even compile:
>
> The method add(T) in the type ArrayList<T> is not
> applicable for the arguments (String)
Thats because your ListenedList class declares the addElement method as :
addElement(String s)
But it should be
addElement(T t)
Message was edited by:
IanSchneider
That's because you're passing a String to it, not a T. Just because T is a String in your current main method doesn't mean it has to be in *every* code that uses this.
And to forestall your next question, your MyListener elementAdded method takes a String. Think about what the type parameter means: some unknown type. Some unknown type != String.
new String("don't declare Strings this way");
> don't declare Strings this wayThat's a rule of thumb. Sometimes it's appropriate.
> > don't declare Strings this way> > That's a rule of thumb. Sometimes it's appropriate.Eh? Like when?I could guess at one potential case, though it's pretty esoteric, and I'm not sure it's even valid.
jverda at 2007-7-13 23:04:03 >

> Just because T is a String in your current main method doesn't mean it has to be in *every* code that uses this.
Yes, I see that. That's what makes ListenedList objects "generic".
Right?
> Some unknown type != String.
That's what I don't see.
The "element" being passed is indeed a String, isn't it?
In any event, hoping to fix it by random selection and then going backwards to figure it out, I've tried every permutation I can think of but I can't seem to fix the code.
public class ListenedList<T> extends ArrayList<T> {
MyListener<T> myListener;
public void addMyListener(MyListener<T> myListener ) {
this.myListener = myListener;
}
public void addElement(T element) {
add(element);
if (myListener != null)
myListener.elementAdded(element);
}
public static void main(String[] args) {
ListenedList<String> list = new ListenedList<String>();
list.addMyListener(new MyListener<String>());
list.addElement("Life is a state of mind");
list.addElement("Why a duck?");
for (int x = 0 ; x < list.size() ; x++)
System.out.println(list.get(x));
/* String lessons */
System.out.println("This" == "This");
System.out.println(new String("This") == ("This"));
}
}
class MyListener<T> {
public void elementAdded(T element) {
System.out.println("\"" + element + "\"" + " was added to the list.");
}
}
> Yes, I see that. That's what makes ListenedList
> objects "generic".
> Right?
Right.
> > Some unknown type != String.
>
> That's what I don't see.
> The "element" being passed is indeed a String, isn't
> it?
In your main method. How about in my main method that loads an instance of your class?
> In any event, hoping to fix it by random selection
> and then going backwards to figure it out, I've tried
> every permutation I can think of but I can't seem to
> fix the code.
Think, don't fiddle. You'll figure it out ;-)
> > Some unknown type != String.
>
> That's what I don't see.
> The "element" being passed is indeed a String, isn't
> it?
class ListenedList<T> extends ArrayList<T>
{
MyListener myListener = new MyListener();
public void addMyListener(MyListener myListener )
{
this.myListener = myListener;
}
public void addElement(String element)
{
add(element);
myListener.elementAdded(element);
}
}
If someone declares ListenedList<Foo> list = new ListendList<Foo>(); then that means that you can only add() Foos to that, but here you are calling add(String) to a list that might have been created to hold Foos.
jverda at 2007-7-13 23:04:03 >

Have a think about what happens if you add this class and try to run its main method:
public class Kerboom {
public static void main(String[] args)
{
ListenedList<Integer> list = new ListenedList<Integer>();
list.addMyListener(new MyListener());
list.addElement(7);
list.addElement(42);
for (int x = 0 ; x < list.size() ; x++)
System.out.println(list.get(x));
}
}
Got it.Thanx to you all.Life is a state of mind.
>Eh? Like when?
> I could guess at one potential case, though it's pretty esoteric, and I'm not sure it's even valid.
I've been a studyin' on it and my state of mind is still not OO.
I thought I would start with a simple object and then substitute more complex objects.
But not having to worry about that is the main feature of OO, isn't it?
> Think, don't fiddle. You'll figure it out ;-)
I was going to give similar advice (only with more sarcasm and condescension), but then I realized I do some of my best thinking while fiddling* or while observing the results of fiddling**.
* Not like that.
** Ewww! Really not like that.
> >Eh? Like when?
>
> > I could guess at one potential case, though it's
> pretty esoteric, and I'm not sure it's even valid.
>
> I've been a studyin' on it and my state of mind is
> still not OO.
> I thought I would start with a simple object and then
> substitute more complex objects.
> But not having to worry about that is the main
> feature of OO, isn't it?
Are you saying that you're declaring strings as new String("abc") rather than just "abc" so that when you start using other classes, you just change the word String and the contents of the parens, but leave the structure of your code intact?
I guess that's valid reasoning, though for me personally, the practical considerations of the difference between = "abc"
and
= new Something(...)
vs. the differences between = new String("abc")
and
= new Something(...)
are negligible.
> ** Ewww! Really not like that.Yeah, I know it looks like making sausage.What can I say?I'm hopelessly stubborn when it comes to how I learn.But I do get there ultimately.
If ListenedList extends ArrayList, wouldn't it be better to override 'add' than to create a separate addElement? Otherwise, you could inadvertently add an element to the underlying list without triggering the listener. Unless you want that to be possible, you should change:
public void addElement(T element) {
add(element);
if (myListener != null)
myListener.elementAdded(element);
}
to
public void add(T element) { // CHANGE NAME TO add
super.add(element); // CALL super'S IMPLEMENTATION
if (myListener != null)
myListener.elementAdded(element);
}
> > ** Ewww! Really not like that.
>
> Yeah, I know it looks like making sausage.
> What can I say?
> I'm hopelessly stubborn when it comes to how I
> learn.
> But I do get there ultimately.
I wasn't referring to you at all with that, but I'll drop it now, because it's really not worth going into.
> I wasn't referring to you at all with that, but I'll drop it now, because it's really not worth going into.
Agreed.
Have a nice day.
> could inadvertently add an element to the underlying list without triggering the listener
Good point.
Again, thanx to one and all.
> If ListenedList extends ArrayList, wouldn't it be better to override 'add' than to create a separate addElement?
After thinking on it some more, wouldn't it be even better for MyListener to be an interface.
Then, have ListenedList both extends ArrayList and implements MyListener?
That doesn't make much sense to me unless your ListenedList IS both an ArrayList and a listener.
> I could guess at one potential case, though it's pretty esoteric, and I'm not sure it's even valid.It can only possibly be useful when interning is a positively bad thing. Would your esoteric case involve using IdentityHashMap<String, Void> as an IdentityMultiSet?
> > I could guess at one potential case, though it's
> pretty esoteric, and I'm not sure it's even valid.
>
> It can only possibly be useful when interning is a
> positively bad thing.
That's along the lines of what I was thinking.
> Would your esoteric case
> involve using IdentityHashMap<String, Void> as an
> IdentityMultiSet?
Nope. I don't even know what an IdentityMultiSet is. (I'll RTFM later.)
I was thinking in terms of GC. Two situations pop to mind, but I'm not really sure if either one is really applicable here.
tinyString = reallyBloodyHugeString.substring(0,1);
I think I read that substring will share the big string's character array, leaving you with a big chunk of memory that can't be GCed even though you're only using a little bit of it. I don't know for sure that this is the case, and I don't know if new String*** it will correct that or if it will still share the array.
The other case has to do with generational GC. I know it's generally better practice to create new collections rather than clear()*** them. If the collection is long-lived but the objects it refers to are short-lived, then you can end up with a collection in an older generation referring to objects in a younger generation, which can force those objects to be promoted, or somesuch similar thing that can slow down GC.
I thought a similar thing might exist with Strings, where rather than doing str1=str2, it might be better to do str1=new String(str2) if str1 will outlive str2, so that there's not that generation crossing between the string and its char array.
Again, though, I haven't looked into it deeply enough to know if it's really valid. Just some speculation.
>> After thinking on it some more, wouldn't it be even better for MyListener to be an interface.
>> Then, have ListenedList both extends ArrayList and implements MyListener?
> That doesn't make much sense to me unless your ListenedList IS both an ArrayList and a listener.
Well, I've always thought of interfaces in terms of DOES rather than IS.
What's wrong with ListenedList being designed to also listen?
Isn't that what one does when creating a GUI class like:class MyGUI extends JFrame implements ActionListener
{
.
.
.
}
> Well, I've always thought of interfaces in terms of
> DOES rather than IS.
It's fine to think of interfaces in terms of defining what a given type does, but when deciding to implement an interface, the primary criteria should be the is-a test: Does it make sense to say this class IS-A Listener.
Implementing is inheritance, just like extending is, and the purpose for inheritance is for specialization of type. The whole notion of inheritance is built around the idea that a Child is-a Parent and hence can be used where a Parent is required. You can certainly use it in other cases, and make it work, but it tends to make code hard to understand and hard to maintain.
Now, there's a good chance dizzy (a.k.a. UJ) will come oozing in here, gibbering against the notion that the is-a test is relevant when deciding on implementing an interface and spewing her noxious bile at anyone who dares to disagree with her. She'll posture as if she's a visionary and dissenters are brain-dead troglodytes, but she's not a visionary, she just likes to be contrary.
I'm not going to get involved in that **** now. All I can say is use your own judgement. Every indivdual and reference that I respect says more or less the same thing as what dcminter and I are saying. UJ is the only one I've ever seen supporting her views.
I'm still waiting for her wrath.
> I'm still waiting for her wrath.I think you're after yawmark. I got a couple of emails from him today, so apparently she hasn't quite finished him off* yet.* Not like that, AFAIK.
> . . . the primary criteria should be the is-a test: Does it make sense to say this class IS-A Listener.
I think yes - if I want ListenedList objects to be able to monitor growth in the list.
In light of that, the question now is, why wouldn't it make sense ala dcminter's comment in reply #22?
Message was edited by:
ValentineSmith
> > . . . the primary criteria should be the is-a test:
> Does it make sense to say this class IS-A Listener.
>
> I think yes - if I want ListenedList objects to be
> able to monitor growth in the list.
> In light of that, the question now is, why wouldn't
> it make sense ala dcminter's comment in reply #22?
ListenedList is the List, why would it be listening for changes? More to the point, go look at the Observer pattern. In your case the ListenedList is the Observable and MyListener is the Observer. Why would you want your Observable to be an Observer in this context? The only reason I can see that you would ever want to do that is if you had a second ListenedList wrapping another ListenedList where the List being wrapped can be modified by means other than going through the wrapper. However, that's not the case with the examples you've shown.
I haven't examined your code carefully but it certainly seems like this is about what you're after:
public interface ListListener<E> {
public void elementAdded(List<? extends E> source, E element);
public void elementRemoved(List<? extends E> source, E element);
}
public final class ObservableList<E> implements List<E> {
private final List<ListListener><? extends E>> listeners;
private final List<E> wrappedList;
public ObservableList(final List<E> wrappedList) {
super();
this.wrappedList = wrappedList;
}
public boolean add(final E o) {
final boolean added = wrappedList.add(o);
if (added) {
elementAdded(o);
}
return added;
}
// Do something similar for the other methods.
public void addListListener(final ListListener<? extends E> listener) {
listeners.add(listener);
}
public void removeListListener(final ListListener<? extends E> listener) {
listeners.remove(listener);
}
private void elementAdded(final E element) {
for (ListListener<? extends E> list : listeners) {
list.elementAdded(this, element);
}
}
private void elementRemoved(final E element) {
for (ListListener<? extends E> list : listeners) {
list.elementRemoved(this, element);
}
}
}
Then the client would have to pass it the List implementation it wants to use and it would be the clients responsibility to not allow changes through the original List or suffer the consequences.
> ListenedList is the List, why would it be listening for changes?
Well, let's say someone places a dozen chesseburgers on a table in front of me.
I may be dumb enough to need a BellyAcheException thrown if I eat more than four or five of them.
Yes, I could use a fixed size array but I may want the benefits of a Collection.
Message was edited by:
ValentineSmith
> > ListenedList is the List, why would it be listening
> for changes?
>
> Well, let's say someone places a dozen chesseburgers
> on a table in front of me.
> I may be dumb enough to need a BellyAcheException
> thrown if I eat more than four or five of them.
> Yes, I could use a fixed size array but I may want
> the benefits of a Collection.
That's nonsensical in this context. It is in no way shape or form an argument for having the ListenedList be a listener.
The reason being if you were going to throw an exception you would do so when they attempt to add it. You would not be listening to yourself in order to throw a worthless exception that won't propagate when something gets added to yourself.
As I said before, if you were wrapping a ListenedList with another ListenedList (or in my example it's an ObservableList) there would be a reason. However, you are not doing that and you have yet to mention anything about doing it.
Let me point out this all began as a mere language exercise.
I just wanted to see if I could produce the same result using an interface instead of a class.
The interface version has fewer lines of code and seems more general.
The class version uses "composition" rather than "inheritance".
So, given the limited scope and questionable logic of this (pun intended) example,
which would be considered better code design - a MyListener class or a MyListener interface?
> Nope. I don't even know what an IdentityMultiSet is. (I'll RTFM later.)
You won't find a class by that name in any library I've used. I was positing it as a collection which can hold multiple copies of objects which are equals()-equal provided the copies are not identity-equal. Don't ask why it would be useful.
Your thoughts on GC are beyond my depth, but I think I see what you're getting at. Interesting.