Reputation: 3389
I have an ArrayList witch constains some instances of different classes ,I want to iterate over a specific type of classes(for example classes tha extend OnRender.class) in a for loop; I have a working code but the code breaks the OO rules resulting in some warnings. Can I make this happen or I will be always break OO by downcasting.
Looking back at what I wrote ,I see that I didnt explained it very good ,so check the code below to see what I mean
the code:
public class ListenerManager{
List<Object> listeners=new ArrayList<Object>();
int it;
Class clazz;
public <T extends Object> T begin(Class<T> clazz){
it=-1;
this.clazz=clazz;
return next();
}
public <T extends Object> T next(){
while((++it)<listeners.size()){
if (clazz.isInstance(listeners.get(it))){
return (T)listeners.get(it);
}
}
return null;
}
public void add(Object listener){
listeners.add(listener);
}
}
I use it like that:
ListenerManager lm=new ListenerManager();
lm.add(new OnRenderListener(){
........
});
for(OnRenderListener orl=lm.begin(OnRenderListener.class);orl!=null;orl=lm.next){
.......
}
the warning I get:
Class clazz;
-> Class is a raw type. References to generic type Class should be parameterized
return (T)listeners.get(it);
-> Type safety: Unchecked cast from Object to T
I don't want to suspend the warnings with just a Suspecnd annotation,I want to follow the OO rules.
Upvotes: 0
Views: 201
Reputation: 2015
This is only a rough (not compilable) way I would do it
public interface Listener{ public boolean canHandle(Event ev); public void handle(Event ev); } public class OnRenderListener implements Listener { public boolean canHandle(Event ev){ return ev instanceOf OnRenderEvent; // or look for the classname } public void handle(Event ev); } public class KeyPressListener implements Listener { public boolean canHandle(Event ev){ return ev instanceOf KeyPressEvent; // or look for the classname } public void handle(Event ev){ keyPressed(((KeyPressEvent)ev).getKey()); } keyPressed(int key){....} } public class Event{} public class KeyPressEvent extends Event{ int key; } public class OnRenderEvent extends Event{ //other stuff } //Loop to fire an event for(Listener lis : listeners){ if(lis.canHandle(currentEvent){ lis.handle(currentEvent); } }
Upvotes: 0
Reputation: 24444
Your ListenerManager
should either return a java Iterator
, or a filtered Collection
(or List
). Don't rebuild a iterator concept on your own. Especially having a begin
and next
on your manager class will introduce unnecessary states there.
With Java 8, your ListenerManager
can easily be writte as:
public class ListenerManager {
private final List<Object> listeners = new ArrayList<Object>();
public <T> Collection<T> listenersOf(Class<T> type) {
return listeners.stream()
.filter(type::isInstance)
.map(type::cast)
.collect(Collectors.toList());
}
public void add(Object listener) {
listeners.add(listener);
}
}
If you use a Java version prior to 8, use a good-old for-each
instead:
public <T> Collection<T> listenersOf(Class<T> type) {
List<T> result = new ArrayList<T>();
for (Object l : listeners) {
if (type.isInstance(l)) result.add(type.cast(l));
}
return result;
}
Upvotes: 1
Reputation: 272217
If you want to identify particular classes or interfaces, use instanceof:
for (Object o : listeners) {
if (o instanceof X) {
X x = (X)o;
}
}
However this isn't very good at all. You really want to leverage the inheritance and polymorphism of the language. My first thought is that your list should container only Listeners
or subtypes e.g.
listeners = new ArrayList<Listener>();
and then you don't require the cast. Everything you pull out from the list will be a Listener (or subtype) and typed as such:
for (Listener l : listeners) {
// listener type functionality here...
}
The Visitor pattern is an option here too. You could pass your visitor object into each listener, and each listener would decide what to do based on its type. The advantage is that as you add subtypes you have to add the appropriate methods - there's no danger of omitting a type from a sequence of class declarations.
Visitor v = new OnlyInterestedInOneTypeOfListener();
for (Listener l : listeners) {
l.useVisitor(v); // different subclasses will call different methods
// on the visitor. Some may be no-ops for different visitor
// implementations
}
(as an aside, I note that you want to discover entries that implement a particular method. This is called duck-typing, and Java doesn't support it. Other languages such as Scala do)
Upvotes: 3
Reputation: 693
You can use Guava's Iterables.filter() method. It will iterate only on the subtype you want.
for (YourType filteredElement : Iterables.filter(listeners, YourType.class)) {
doSomething(filteredElement);
}
Upvotes: 2