O
Oliver Wong
I'm using the adapter pattern, and I've got a factory to generate adapters
for passed in adaptees. Let's call the class of the objects getting adapted
"Foo", and the Adapter class itself "Bar".
So here are some details specific to my situation:
1) the constructor for Bar is private and visible to the factory (Bar is
acting as its own factory), so I can completely control when and how Bar
gets instantiated.
2) If an existing adapter for a given instance of Foo doesn't exist yet,
I'll want to create a new Bar which matches with it.
3) If there already is a adapter h for Foo, I have to return that existing
match, and NOT generate a new one.
4) upon a request for an adapter for null, I return null.
This is relatively easy to do if I have infinite memory. I just create a
Map<Foo,Bar>, like so:
<code>
public class Bar {
private final Foo f;
private Bar(Foo f) {
this.f = f;
}
private final static Map<Foo,Bar> mapping = new
ConcurrentHashMap<Foo,Bar>();
public static Bar make(Foo f) {
if (impl == null) {
return null;
}
synchronized (mapping) {
if (!mapping.containsKey(f)) {
mapping.put(f, new Bar(f));
}
Bar b = mapping.get(f);
assert returnValue != null;
return returnValue;
}
}
public void newInterfaceWhichModifiesState() {
this.f.oldInterfaceWhichModifiesState();
}
public int newInterfaceWhichGetsState() {
return this.f.oldInterfaceWhichGetsState();
}
}
</code>
However, it's possible that the calling code is generating billions and
billions of instances of Foo, and then throwing them away after their first
use. My Map would prevent the garbage collector from being able to reclaim
those instances. I can safely delete those "throwaway Foos" and their
matches from my Map, because if there doesn't exist a reference to some
instance of Foo anywhere else in the JVM, then it can't possibly ever occur
that that instance will ever get passed into my make(Foo) method, in which
case, I would never need to return its corresponding Bar.
The two potential-solutions I looked at, Maps of WeakReferences and
WeakHashMap, turned out not to satisfy my requirements.
If instead of a Map<Foo,Bar>, I had a Map<Foo,WeakReference<Bar>>, then it's
possible that the matching Bar would get garbage collected, but the instance
of Foo would get passed in again, and there's no way for me to recover its
matching pair, thus violating condition (3) mentioned above.
If I replace Map<Foo,Bar> with WeakHashMap<Foo,Bar>, none of the keys will
get GCed, because instances of Bar contain a strong reference to Foo, via
the private field f. If I change that field to a weak reference, then it's
possible the instance of Foo that's being adapted will get GCed while the
corresponding adapter is still in use, resulting in the newInterface()
method failing.
I think what I need is some sort of special PairOfWeakReference class such
that if there are any references to either an instance of Foo or its
corresponding Bar, then BOTH remain uncollectable. However, once there do
not exist any strong references to either instances, then the pair become
collectable simultaneously.
Using a pair class, as in:
<code>
public class Pair<A,B> {
public final A a;
public final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
}
</code>
WeakReference<Pair<Foo,Bar>> won't work either, because the WeakReference
class is checking against references to instances of the Pair class, rather
than references to the instances of Foo and Bar.
This adapter-factory combination doesn't sound like something that unusual
or obscure, so I figure I must be missing something simple from effectively
implementing it. Can anyone tell me what that is?
- Oliver
for passed in adaptees. Let's call the class of the objects getting adapted
"Foo", and the Adapter class itself "Bar".
So here are some details specific to my situation:
1) the constructor for Bar is private and visible to the factory (Bar is
acting as its own factory), so I can completely control when and how Bar
gets instantiated.
2) If an existing adapter for a given instance of Foo doesn't exist yet,
I'll want to create a new Bar which matches with it.
3) If there already is a adapter h for Foo, I have to return that existing
match, and NOT generate a new one.
4) upon a request for an adapter for null, I return null.
This is relatively easy to do if I have infinite memory. I just create a
Map<Foo,Bar>, like so:
<code>
public class Bar {
private final Foo f;
private Bar(Foo f) {
this.f = f;
}
private final static Map<Foo,Bar> mapping = new
ConcurrentHashMap<Foo,Bar>();
public static Bar make(Foo f) {
if (impl == null) {
return null;
}
synchronized (mapping) {
if (!mapping.containsKey(f)) {
mapping.put(f, new Bar(f));
}
Bar b = mapping.get(f);
assert returnValue != null;
return returnValue;
}
}
public void newInterfaceWhichModifiesState() {
this.f.oldInterfaceWhichModifiesState();
}
public int newInterfaceWhichGetsState() {
return this.f.oldInterfaceWhichGetsState();
}
}
</code>
However, it's possible that the calling code is generating billions and
billions of instances of Foo, and then throwing them away after their first
use. My Map would prevent the garbage collector from being able to reclaim
those instances. I can safely delete those "throwaway Foos" and their
matches from my Map, because if there doesn't exist a reference to some
instance of Foo anywhere else in the JVM, then it can't possibly ever occur
that that instance will ever get passed into my make(Foo) method, in which
case, I would never need to return its corresponding Bar.
The two potential-solutions I looked at, Maps of WeakReferences and
WeakHashMap, turned out not to satisfy my requirements.
If instead of a Map<Foo,Bar>, I had a Map<Foo,WeakReference<Bar>>, then it's
possible that the matching Bar would get garbage collected, but the instance
of Foo would get passed in again, and there's no way for me to recover its
matching pair, thus violating condition (3) mentioned above.
If I replace Map<Foo,Bar> with WeakHashMap<Foo,Bar>, none of the keys will
get GCed, because instances of Bar contain a strong reference to Foo, via
the private field f. If I change that field to a weak reference, then it's
possible the instance of Foo that's being adapted will get GCed while the
corresponding adapter is still in use, resulting in the newInterface()
method failing.
I think what I need is some sort of special PairOfWeakReference class such
that if there are any references to either an instance of Foo or its
corresponding Bar, then BOTH remain uncollectable. However, once there do
not exist any strong references to either instances, then the pair become
collectable simultaneously.
Using a pair class, as in:
<code>
public class Pair<A,B> {
public final A a;
public final B b;
public Pair(A a, B b) {
this.a = a;
this.b = b;
}
}
</code>
WeakReference<Pair<Foo,Bar>> won't work either, because the WeakReference
class is checking against references to instances of the Pair class, rather
than references to the instances of Foo and Bar.
This adapter-factory combination doesn't sound like something that unusual
or obscure, so I figure I must be missing something simple from effectively
implementing it. Can anyone tell me what that is?
- Oliver