开发者

A way to find what a Java object was initialized as instead of declared type?

I don't know if I'm missing something here, but I am having trouble casting an object to its actual, initialized type. Basically, if I create an object with "SuperClass sc = new SubClass()," then I call a method on sc, I want the method to be able to call method(Subclass) instead of method(Superclass). Example shown below:

public class Example
{
    public static void act(SuperClass a) {
        System.out.println("SuperClass");
    }

    public static void act(SubClass a) {
        System.out.println("SubClass");
    }

    public static void main(String[] args) {
        SuperClass sc = new SubClass();

        // want to find a way to call act(SubClass) instead of act(SuperClass)
        act(sc);
   }
}

class SuperClass {}
class SubClass extends SuperClass {}

I am using the visitor pattern right now, but I'm wondering if there are other ways to do this, maybe via the Java Reflection API?

Thanks alot in advance!

== edit ==

I know that generally with OO it's better to stick the functionality back to the superclass/subclasses themselves, but for my specific use cases I have a bunch of subclasses that are immutable model classes, which should be passed to different kinds of execution engines (think different "Example" classes). The subclasses/model classes should only hold immutable information, nothing more, and the actual real functionality lies with the execution engine (Example class). That's why I am wondering about alternatives to the visitor's pattern. Does anyone have a way to recover the actual "initialized" information in Java? If so, thank you so much.

And because of the nature of the problem, I can't use direct casting... Imagine if I have an arraylist of SuperClass, where each element may be SubClass1, SubClass2, SubClass3, all extending from SuperClass.

Now, when you pull things out of the Arraylist, you get a SuperClass object, even though they may really be SubClass1, SubClass2, SubClass3, etc.

Next, I want to call act(SubClass1), and be able to invoke the correct act() method on the current type. So I want to end up calling act(SubClass1), act(SubClass2), act(SubClass3), instead of act(SuperClass).

== edit again ==

I've came up with a way of doing this via the Java Reflection API, by finding the actual underlying type of the SubClass using Class.forName(classname), then dynamically invoking the method with the correct method signature. I h开发者_开发问答ave written this up in answer form somewhere down this page for those who are interested about this problem. Note that this isn't a very efficient way of accomplishing what I'm trying to do here, and you're probably better off with visitor pattern or if-else statements if you're stuck with my situation.


So the answer that Nicola Musatti gave is the closest to answering my question, though as he has also pointed out, as the number of SubClasses grow the if-else statements list gets very long. I will choose his answer as the accept answer since I hadn't stated clearly in my question that I was hoping to avoid the if-else checks.

Anyways, so I've played around a bit with the Java Reflection API today and came up with this:

SuperClass sc = new SubClass();

// Get the actual class of sc. actualClass now is SubClass.
Class actualClass = Class.forName(sc.getClass().getCanonicalName());

// Basically invoking act(SubClass sc) instead of act(SuperClass sc)
Class parameters[] = {actualClass};
Method method = Example.class.getMethod("act", parameters);
Object arguments[] = {sc};
method.invoke(null, arguments);

This is surely not a great way to do things, especially because of the performance penalty impose by the Java Reflection API. This might be better than visitor pattern or the if-else checks if you have a million subclasses, since it's probably less code to manage, however I'll stick with the visitor pattern for now since I don't have a million subclasses to manage.

Regardless, just thought I would post this here to show that it can be done, just for those who are curious.


Shouldn't you define act() in SuperClass and SubClass? That way the correct method will be called regardless of the type of the reference to the object.

Edit: If I remember correctly the visitor pattern defines something like an accept() method on the elements to be visited which allows the visitor to polymorphically access whatever it's interested in in the visited elements.


The general solution is the Visitor pattern. If you have a specific situation where you know the actual type of sc you can indeed use a cast, as was already suggested, possibly preceded by a type check as in

if ( sc instanceof SubClass ) act((SubClass)sc);

However the Visitor pattern was invented because this approach doesn't scale up when the number of classes you need to handle grows.

Last but not least, sometimes the simplest approach is just to keep around a variable of the actual type:

SubClass scc = new SubClass();
SuperClass sc = scc;
act(scc);


What about

act((SubClass)sc);

??


Maybe you should make the method act member of the SupperClass class, and then override it in SubClass class.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜