I have the following class structure:
DataStore classes:
PStore
: public abstract class PStore ...
CStore1
and CStore2
CStore1.getInstance(Id)
and CStore2.getInstance(Id)
Worker classes:
public abstract class PWork...
CWork1
and CWork2
Current situation:
Class CWork1
implemented an inner class with the following code accessing the Store:
protected class fListener implements ActionListener { public void actionPerformed(ActionEvent ae) { CStore1.getInstance(Id).someMethod(); # NOTE: someMethod() is implemented in CStore2 as well } }
I need to implement the same fListener
class in CWork2
Being a good little software developer, instead of simply copy/pasting fListener
from CWork1
to CWork2
class and then changing CStore1
to CStore2
in its code, I decided like an idiot to migrate it to a parent class.
What I tried to do:
Create an abstract method in PStore
called getStoreInstance(int Id)
: public abstract PStore getStoreInstance(int Id);
Implement that method in CStore1
as public PStore getStoreInstance(int Id) { return CStore1.getInstance(Id); }
public PStore getStoreInstance(int Id) { return CStore1.getInstance(Id); }
So far, so good. It compiled all 3 Store classes.
Copy the fListener code from PWork
to CWork1
class
Obviously, the compiler complained that it does not know anything about CStore1 class and its method getStoreInstance()
Now, I decided to simply call the newly created getStoreInstance()
:
Old code: CStore1.getInstance(Id).someMethod();
New code: PStore.getStoreInstance(Id).someMethod();
Compiler complaint : "Cannot make a static reference to the non-static method getStoreInstance(Id) from the type PStore".
The only "fix" proposed by Eclipse was to make getStoreInstance() method static, but THAT didn't work either:
When I added a static
modifier to the abstract classes's declaration of the method: public static abstract PStore getStoreInstance(int Id);
- the compiler complained that "The abstract method getStoreInstance in type PStore can only set a visibility modifier, one of public or protected"
When I tried to make the method non-abstract: public static PStore getStoreInstance(int Id);
it complained that the method needed a body (quite sensibly)
When I tried to add a body, it required me to return PStore object... BUT!!!! My abstract class PStore has no constructore, so there's no way I can return PStore object!!!
Am I approaching the problem correctly?
If not, how should I approach it?
If I am, then how can I resolve the original complaint about "Cannot make a static reference to the non-static method", in light of the fact that the abstract class isn't letting me make that method static?
The way it looks to me, you have two class hierarchies that are isomorphic (if I'm using the right term) and there is a relationship between the corresponding classes:
PStore PWork
/ \ / \
/ \ / \
CStore1 CStore2 CWork1 CWork2
^ ^ ^ ^
| | | |
|---------+--------------| |
| |
|---------------------|
But I don't think there's a built-in way to express this kind of relationship in Java. Somehow, you're going to have to do something that makes those relationships explicit. Ie there will need to be something in CWork1
that refers to CStore1
, and something in CWork2
that refers to CStore2
, or else some kind of list or map that associates them.
There's probably a design pattern that addresses this structure, but I can't think of it right now. So off the top of my head, here are a couple possible solutions (neither of which uses reflection):
Put the abstract getStoreInstance
method you defined in PWork
instead of PStore
. In CWork1
, override it with a method that calls CStore1.getInstance
, and similarly in CWork2
. Then the common fListener
actionPerformed
code would call getStoreInstance(Id).someMethod()
, with no class name, which would work on the current PWork
instance. You would still have some "duplicated" code (since you'd have to implement getStoreInstance
in both CWork
classes), but it would be kept to a minimum.
Set up a map. This compiles (Java 8), but I haven't tested it:
class PWork { private static Map<Class<? extends PWork>,IntFunction<PStore>> storeMap; static { storeMap = new HashMap<>(); storeMap.put(CWork1.class, CStore1::getInstance); storeMap.put(CWork2.class, CStore2::getInstance); } protected PStore getStoreInstance(int Id) { return storeMap.get(getClass()).apply(Id); } protected class fListener implements ActionListener { public void actionPerformed(ActionEvent ae) { int Id = ???; // I'm assuming Id is computed from ae somehow?? getStoreInstance(Id).someMethod(); } } }
Now getStoreInstance
looks in the map, using the current PWork
object's class ( CWork1
or CWork2
) as a key, to figure out which getInstance
static method to call.
Yes, you can do it with a static method on PStore and not having repeated code.
Make your worker generic, keeping a class token reference, and invoke
public class Worker<T extends PStore> {
private final Class<T> c;
public Worker(Class<T> c) {
this.c = c;
}
public void actionPerformed(ActionEvent ae) {
PStore.getInstance(c, Id).someMethod();
}
// other methods
}
Now for the static method in PStore, which doesn't have to be a generic method: The worker doesn't have to know the actual type returned, it just will be the right type.
public class PStore {
private static Map<Class<?>, List<PStore>> map = new HashMap<>();
protected PStore() {
List<PStore> list = map.get(getClass());
if (list == null) {
list = new ArrayList<>();
map.put(getClass(), list);
}
list.add(this);
}
public static PStore getInstance(Class<? extends PStore> c, int i) {
return map.get(c).get(i);
}
public abstract void someMethod();
}
The benefit here is that the capping of N instances is left to the PStore subclass. Also notice how the list contains PStore instances, which are actually subclass instances, but neither PStore nor the worker cares (as long as it is actually the desired subclass returned, which it will be).
Disclaimer: I typed this in on my phone, so it might not compile - let me know if there's a problem and I'll try to fix it.
The compiler is correct in that making a reference to an instance method in a static context doesn't work. In the static world, you're working on a class level and dealing with properties of entire classes. If you try to call an instance method, how does the compiler know what instance you want to perform the instance method?
Making an abstract method doesn't work because abstract methods must be implemented in subclasses. However, static methods cannot be overridden -- only hidden. So if static
were allowed on abstract methods, and you tried to do AbstractClass.staticMethod()
, what implementation would the runtime use?
Your code is somewhat hard to follow, but here's what I think. Just based on the name, getStoreInstance(int Id)
should indeed be a static method, because it doesn't make much sense for an instance to get another instance; that seems more like something that the class should do. You might want to rethink how you're structuring your code... From the code names, it seems like you should have a class-level way of tracking instances of the class, so maybe doing something like that and implementing getInstance()
and getStoreInstance()
as static methods would be a start?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.