Workaround to overcome the generics limitation causes by Erasure

Y

yancheng.cheok

Initially, I thought the introduce of generics in Java would solve me
some problem in pattern design. Here, I make use of generic to design
Observer/Subject pattern.

public interface Observer<S, A> {
public void update(S subject, A arg);

}

public class Subject<S, A> {

public void attach(Observer<S, A> observer) {
observers.add(observer);
}

void notify(S subject, A arg) {
for (Observer<S, A> obs : observers) {
obs.update(subject, arg);
}
}

private List<Observer<S, A>> observers = new
CopyOnWriteArrayList<Observer<S, A>>();

}

However, due to the limitation of generics causes by erasure, I am
unable to have two different instantiations with same generics
interface

http://angelikalanger.com/GenericsF...tiations of the same parameterized interface?

Compiler will complain if I tend to make an Object act as more than
one type of Observer.

public class MainFrame extends javax.swing.JFrame implements
org.yccheok.jstock.engine.Observer<RealTimeStockMonitor,
java.util.List<org.yccheok.jstock.engine.Stock>>,
org.yccheok.jstock.engine.Observer<StockHistoryMonitor,
StockHistoryServer>

Currently, my workaround for this is using

public class MainFrame extends javax.swing.JFrame implements
org.yccheok.jstock.engine.Observer<Object, Object>

and performing instanceof checking (to check whether it is
RealTimeStockMonitor or StockHistoryMonitor) during runtime.

I had gone through severals articles :

http://www-128.ibm.com/developerworks/java/library/j-jtp01255.html
http://gafter.blogspot.com/2004/09/puzzling-through-erasure-answer.html
http://gafter.blogspot.com/2006/11/reified-generics-for-java.html

Till now, I still do not have any better workaround, in order to have
a typed-safe Observer/ Subject pattern. Can anyone of you share your
experience, how do you overcome this situation?

Thank you very much!
 
D

Daniel Pitts

Initially, I thought the introduce of generics in Java would solve me
some problem in pattern design. Here, I make use of generic to design
Observer/Subject pattern.

public interface Observer<S, A> {
public void update(S subject, A arg);

}

public class Subject<S, A> {

public void attach(Observer<S, A> observer) {
observers.add(observer);
}

void notify(S subject, A arg) {
for (Observer<S, A> obs : observers) {
obs.update(subject, arg);
}
}

private List<Observer<S, A>> observers = new
CopyOnWriteArrayList<Observer<S, A>>();

}

However, due to the limitation of generics causes by erasure, I am
unable to have two different instantiations with same generics
interface

http://angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.h...

Compiler will complain if I tend to make an Object act as more than
one type of Observer.

public class MainFrame extends javax.swing.JFrame implements
org.yccheok.jstock.engine.Observer<RealTimeStockMonitor,
java.util.List<org.yccheok.jstock.engine.Stock>>,
org.yccheok.jstock.engine.Observer<StockHistoryMonitor,
StockHistoryServer>

Currently, my workaround for this is using

public class MainFrame extends javax.swing.JFrame implements
org.yccheok.jstock.engine.Observer<Object, Object>

and performing instanceof checking (to check whether it is
RealTimeStockMonitor or StockHistoryMonitor) during runtime.

I had gone through severals articles :

http://www-128.ibm.com/developerwor...ot.com/2006/11/reified-generics-for-java.html

Till now, I still do not have any better workaround, in order to have
a typed-safe Observer/ Subject pattern. Can anyone of you share your
experience, how do you overcome this situation?

Thank you very much!

public interface Observer<S, A> {
void update(S subject, A arg);
}

Where does the compiler complain about here:

class MyObserver {
public void update(Foo subject, Integer arg) {
}

public void update(Bar subject, String arg) {
}

public Observer<Foo, Integer> getFooObserver() {
return new Observer<Foo, Integer>() {
public void update(Foo subject, Integer arg) {
MyObserver.this.update(subject, arg);
}
}
public Observer<Bar, Integer> getBarObserver() {
return new Observer<Bar, Integer>() {
public void update(Bar subject, Integer arg) {
MyObserver.this.update(subject, arg);
}
}
}

Hope this helps,
Daniel.
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

(e-mail address removed) schreef:
Initially, I thought the introduce of generics in Java would solve me
some problem in pattern design. Here, I make use of generic to design
Observer/Subject pattern.

public interface Observer<S, A> {
public void update(S subject, A arg);

}

public class Subject<S, A> {

Have you thought about why you have the S parameter here? I have the
intuition it is unnecessary: it is Subject itself right?
public void attach(Observer<S, A> observer) {
observers.add(observer);
}

void notify(S subject, A arg) {
for (Observer<S, A> obs : observers) {
obs.update(subject, arg);
}
}

private List<Observer<S, A>> observers = new
CopyOnWriteArrayList<Observer<S, A>>();

}

However, due to the limitation of generics causes by erasure, I am
unable to have two different instantiations with same generics
interface

http://angelikalanger.com/GenericsF...tiations of the same parameterized interface?

Compiler will complain if I tend to make an Object act as more than
one type of Observer.

Of course. Imagine one extends implements List<Integer> and
List said:
public class MainFrame extends javax.swing.JFrame implements
org.yccheok.jstock.engine.Observer<RealTimeStockMonitor,
java.util.List<org.yccheok.jstock.engine.Stock>>,
org.yccheok.jstock.engine.Observer<StockHistoryMonitor,
StockHistoryServer>

Currently, my workaround for this is using

public class MainFrame extends javax.swing.JFrame implements
org.yccheok.jstock.engine.Observer<Object, Object>

and performing instanceof checking (to check whether it is
RealTimeStockMonitor or StockHistoryMonitor) during runtime.

Why don’t you just have two observers, each doing its function? You
have a method addObserver(Observer) right?

Please show some more code, i.e. not only the definition of the
interfaces/classes above, but also how you use them.

In general, every time someone complains about erasure, it is because
they have a flaw in their design.

H.
- --
Hendrik Maryns
http://tcl.sfs.uni-tuebingen.de/~hendrik/
==================
http://aouw.org
Ask smart questions, get good answers:
http://www.catb.org/~esr/faqs/smart-questions.html
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFGOKw1e+7xMGD3itQRAk+/AJ9V+JViq07wrrqT+MhUy4B14YRfEwCZAapy
YGqx4CBu4UG783kFNhvJm/4=
=EvTW
-----END PGP SIGNATURE-----
 
Y

yancheng.cheok

Have you thought about why you have the S parameter here? I have the
intuition it is unnecessary: it is Subject itself right?

public void update(S subject, A arg);

After the observer receive notification from subject, it may need to
access the properties of the subject, in order to get the detail of
the notification message. Yes! we may pass along the notification
message by using the arg. However, this require us to design another
class, just to carry out the notification message. Please see my
sample code in detail later.
have a method addObserver(Observer) right?

Most of the situation, a single observer need to respond to multiple
subjects.

For more detailed explanation, please refer to a C++ version

http://www.codeproject.com/cpp/observer_with_templates.asp


Here is my sample code.

GUIToDisplayTimeAndTemperature is a GUI application. It will receive
notification from MyTemperature and MyTime, whenever there is a
temperature change or time change. Once it receive the notification,
it will access the properties of MyTemperature and MyTime, and update
the GUI display accordingly, using those subject's properties.

Please note that the following code won't compile, due to Erasure

/*** MyTemperature.java ***/
public class MyTemperature extends Subject<MyTemperature, String> {

/** Creates a new instance of MyTemperature */
public MyTemperature() {
}

public void run() {
System.out.println("MyTemperature is doing some time consuming
calculation...");
result = 980;
System.out.println("MyTemperature calculation is done. Going
to notify all the observers");

notify(this, "Some Extra Arguement from MyTime");
}

public int getTemperature() {
return result;
}

private int result = 0;
}

/*** MyTime.java ***/
public class MyTime extends Subject<MyTime, String> {

/** Creates a new instance of MyTime */
public MyTime() {
}

public void run() {
System.out.println("MyTime is doing some time consuming
calculation...");
result = 100;
System.out.println("MyTime calculation is done. Going to
notify all the observers");

notify(this, "Some Extra Arguement from MyTime");
}

public int getTime() {
return result;
}

private int result = 0;
}

/*** GUIToDisplayTimeAndTemperature.java ***/

public class GUIToDisplayTimeAndTemperature implements
Observer<MyTemperature, String>, Observer<MyTime, String> {

/** Creates a new instance of Main */
public GUIToDisplayTimeAndTemperature() {
MyTemperature temperature = new MyTemperature();
MyTime time = new MyTime();

temperature.attach(this);
time.attach(this);

temperature.run();
time.run();
}

public void update(MyTemperature temperature, String arg) {
System.out.println("GUI : temperature is now " +
temperature.getTemperature());
System.out.println("GUI : here is the arg from temperature " +
arg);
}

public void update(MyTime time, String arg) {
System.out.println("GUI : time is now " + time.getTime());
System.out.println("GUI : here is the arg from temperature " +
arg);
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
new GUIToDisplayTimeAndTemperature();
}

}

However, based on the suggestion from Daniel Pitts, we might have a
workaround.

Although it make more sense (and better clean design) to have
GUIToDisplayTimeAndTemperature itself to be the "observer of
temperature" and "observer of time", instead of having an anonymous
class to take that role.

At least suggestion from Daniel Pitts gives something which work :)
Thanks!

public class GUIToDisplayTimeAndTemperature {
// public class GUIToDisplayTimeAndTemperature {

/** Creates a new instance of Main */
public GUIToDisplayTimeAndTemperature() {
MyTemperature temperature = new MyTemperature();
MyTime time = new MyTime();

temperature.attach(getTemperatureObserver());
time.attach(getTimeObserver());

temperature.run();
time.run();
}

public Observer<MyTemperature, String> getTemperatureObserver() {
return new Observer<MyTemperature, String>() {
public void update(MyTemperature temperature, String arg)
{
System.out.println("GUI : temperature is now " +
temperature.getTemperature());
System.out.println("GUI : here is the arg from
temperature " + arg);
}
};
}

public Observer<MyTime, String> getTimeObserver() {
return new Observer<MyTime, String>() {
public void update(MyTime time, String arg) {
System.out.println("GUI : time is now " +
time.getTime());
System.out.println("GUI : here is the arg from
temperature " + arg);
}
};
}

/**
* @param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
new GUIToDisplayTimeAndTemperature();
}

}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,968
Messages
2,570,152
Members
46,697
Latest member
AugustNabo

Latest Threads

Top