Reputation: 5624
I have two loops that I'm trying to refactor into a single method. The loops notify listeners of different types and they are declared as generic. I thought I could do it with some reflection but I can't seem to get my head around it.
Here is my message class and listener interface:
public class Message {
private RequestType type; // Enum
private String data;
...
}
public interface IRequestListener<T> {
public void resultUpdated(T result);
}
Here are the loops:
...
// Notification is an Enum
for (IRequestListener<?> listener : listeners) {
IRequestListener<Notification> notificationListener =
(IRequestListener<Notification>) listener;
notificationListener.resultUpdated(Notification.valueOf(message.getData()));
}
...
for (IRequestListener<?> listener : listeners) {
IRequestListener<Integer> notificationListener =
(IRequestListener<Integer>) listener;
notificationListener.resultUpdated(Integer.valueOf(message.getData()));
}
Here is my casting attempt at solving which is obviously wrong:
...
notifyMessageListeners(Notification.class, essage)
...
notifyMessageListeners(Integer.class, message);
...
private void notifyMessageListeners(Class clazz, Message message) {
LinkedList<IRequestListener<?>> listeners =
requestListenerMap.get(message.getType());
for (IRequestListener<?> listener : listeners) {
// How to solve this cast?
// Error: unknown class: 'clazz'
IRequestListener<clazz> notificationListener =
(IRequestListener<clazz>) listener;
// How to solve the valueOf? Need to cast to a class which has valueOf()?
// Error: cannot resolve method valueOf
notificationListener.resultUpdated(clazz.valueOf(message.getData()));
}
}
Here is my generic attempt at solving which is also wrong:
private void notifyMessageListeners(Message message) {
LinkedList<IRequestListener<?>> listeners =
requestListenerMap.get(message.getType());
for (IRequestListener<?> listener : listeners) {
// Causes error
// resultUpdated (<?>) in IRequestListener cannot be applied to (java.lang.String)
listener.resultUpdated(message.getData());
}
}
I've spent quiet some time on SO and in the docs trying to figure this out. There seem to be many posts about generics but I can't find something that I can relate to my specific use case.
Cheers.
EDIT:
Solved with help of accepted answer.
notifyMessageListeners(message, new IValueGetter<T>() {
public T getValue(String data) {
return (T) Notification.valueOf(data);
}
});
private void notifyMessageListeners(Message message, IValueGetter<T> getter) {
LinkedList<IRequestListener<?>> listeners =
requestListenerMap.get(message.getType());
for (IRequestListener<?> listener : listeners) {
IRequestListener<T> notificationListener = (IRequestListener<T>) listener;
notificationListener.resultUpdated(getter.getValue(message.getData()));
}
}
Upvotes: 1
Views: 267
Reputation: 726529
Here is how to fix your first attempt:
First, define an interface for calling valueOf
(you can avoid this by using reflection, but I think that a "reflectionless" approach is cleaner)
interface ValueGetter<T> {
T getValue(String data);
}
This generic interface lets you pass a piece of code to notifyMessageListeners
so that the method would call you back to extract the desired value from the string.
private void notifyMessageListeners(Message message, ValueGetter<T> getter) {
LinkedList<IRequestListener<?>> listeners =
requestListenerMap.get(message.getType());
for (IRequestListener<?> listener : listeners) {
IRequestListener<T> notificationListener =
(IRequestListener<T>) listener;
notificationListener.resultUpdated(getter.getValue(message.getData()));
}
}
When you call notifyMessageListeners
, you need to pass an instance of ValueGetter<T>
, like this:
notifyMessageListeners(message, new ValueGetter<T>() {
public T getValue(String data) {
return (T) Notification.valueOf(data);
}
});
notifyMessageListeners(message, new ValueGetter<T>() {
public T getValue(String data) {
return (T) Integer.valueOf(data);
}
});
Upvotes: 2