简体   繁体   中英

How can I get an instance of a subclass of abstract class from a static method of another class?

I have the following class structure:

  • DataStore classes:

    • Parent class: PStore : public abstract class PStore ...
    • 2 child classes extending it: CStore1 and CStore2
    • Each of the child classes is a N-glton (sorry, not sure what Java term for that is) - it can have only N instances, numbered 1..NMax.
    • Each child class has the following methods for accessing that instance: CStore1.getInstance(Id) and CStore2.getInstance(Id)
  • Worker classes:

    • Parent class: public abstract class PWork...
    • 2 child classes extending it: 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):

  1. 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.

  2. 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM