How to cast to a polymorphic type with a member not in interface

I have an interface like

public interface IFoo {
    String getX();
    ...
}

and have several classes like

public class Bar implements IFoo {
    public String getX() {
        ...
    }

    public String getY() {
        ...
    }
}

The important thing here is that all of the classes that implement IFoo, like Bar, have the String getY() method, but this method is not part of the IFoo interface.

Now, I have some XML that I need to deserialize to a Java object. The XML can be an object of any of the IFoo implementing classes, but after deserializing, I will need to use the String getY() method.

Are there any concise or elegant ways to achieve this?

I have created another interface like

public interface IFooExtension extends IFoo {
    String getY();
}

and have tried creating an instance of it like

String xml = ...;
IFooExtension fooExtension = (IFooExtension) XMLBeanSerializer.deserialize(xml);

but get an error when casting the XML to the object that says something object Instance of Bar cannot be cast to an object of type IFooExtension

ANSWERS:

  • The answers from GhostCat and Edwin Dalorzo are both great.

    • GhostCat explains that this is not possible in Java out of the box because Java does not support duck typing

    • Edwin Dalorzo provides a workaround for this issue by applying the Adapter design pattern

Answer

From my understanding of your question, you know that XMLBeanSerializer.deserialize(xml) is going to give you a specific kind of object, an object of type A, B or C. Those types you know in advance.

Now, you also know that A, B, and C have a method with signature int getX(), but none of these types have a common interface with that method.

You also know that A, B, and C do have a common interface type Foo that implements int getY(). So, all the objects you deserialize can be assigned to a reference of type Foo and gain access to int getY(), but there is not a type that you can use for all those objects to gain access to both methods int getX() and int getY().

That’s my understanding of your problem and based on that I believe we can implement a solution with an adapter pattern.

So, to start you have a common interface:

interface Foo {
   int getY();
}

And a few deserializable objects from classes that implement it, plus an additional method:

class A implements Foo {
    @Override
    public int getY() { return 1; }
    public int getX() { return 0; }
}

class B implements Foo {
    @Override
    public int getY() { return 2; }
    public int getX() { return 0; }
}

class C implements Foo {
    @Override
    public int getY() { return 3; }
    public int getX() { return 0; }
}

So, how can I deserialize an object of any of these classes and gain access to both methods through a common interface?

Adapter Solution

Well, obviously we can’t do it the way it is. As other answers have pointed out, Java does not have duck typing.

However, we can simply build a new type hierarchy in which the classes implement both methods.

We start with an interface FooExt that exposes both methods.

interface FooExt extends Foo {
   int getX();
}

Then we define an adapter class hierarchy based on this new interface:

class AAdapter implements FooExt {
    private final A adaptee;

    AAdapter(A adaptee) {
       this.adaptee = adaptee;
    }

    @Override
    public int getY() { return adaptee.getY(); }

    @Override
    public int getX() { return adaptee.getX(); }
}

class BAdapter implements FooExt {
  //TODO: implement adapter
}

class CAdapter implements FooExt {
    //TODO: implement adapter
}

And now we can do something like this:

public FooExt deserialize(String xlm) {
   Object obj = XMLBeanSerializer.deserialize(xml);
   if (obj instanceof A a) {
       return new AAdapter(a);
   } 
   if (obj instanceof B b) {
      return new BAdapter(b);
   } 
   if (obj instanceof C c) {
      return new BAdapter(c);
   } 
   throw new AssertionError("Unknown object type: " + obj);
}

As you can see the adapter pattern uses delegation (which is a form of object composition) instead of inheritance to adapt the object to a new interface that support both methods.

This follows that principle of: “Favor composition over inheritance”.

Finally, in the call site we can do somewhat as follows:

FooExt fe = deserialize(xml);
int x = fe.getX(); 
int y = fe.getY();

Proxy Solution

The following is a proxy-based version of the code that would save you the time of having to implement a full class hierarchy.

A proxy pattern can help you wrap the adapted in a reflection handler that can help you find the method you want to invoke.

So, consider the following code to create a proxy around the adapted. The proxy is of type FooExt.

static FooExt createProxyAdapter(Foo adaptee) {
    ClassLoader cl = Foo.class.getClassLoader();
    Object adapter = Proxy.newProxyInstance(cl, new Class<?>[]{FooExt.class},
            (Object proxy, Method method, Object[] args) -> {
                if(method.getName().endsWith("getX")) {
                    //then find the method in the adaptee
                    var m = adaptee.getClass().getMethod("getX");
                    return m.invoke(adaptee);
                }
                //otherwise call the method from FooExt.
                return method.invoke(adaptee, args);
            });
    return (FooExt) adapter;
}

And now you can do somewhat as follows:

Foo a = new A();
FooExt fa = createProxyAdapter(a);

int y = fa.getY();
int x = fa.getX();

Works like a charm!

If there are too many classes to adapt, then this solution is, perhaps, preferable. If there are only a few classes, then I would prefer the first one, since I would consider the code easier to maintain, debug, follow, etc.

Whether you consider these two ideas an elegant solution or not is an entirely different discussion 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *