2

This is not a regular question, please listen to my explain.

I have an object of class Animal. How can I transform it to its subclass Cat? Like this:

Animal a = new Animal();
Cat c = (Cat) a;

Of course, I know It's not possible casting directly from Animal to Cat. But I don't want to manually create a new Cat and copy the fields from Animal to it. Because sometimes I have lots of class hierarchies which need to transform. How can I solve it by a general way?

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
jinge
  • 785
  • 1
  • 7
  • 20

3 Answers3

3

What about creating a copy-constructor in the subclass?

public Cat(Animal animal) { super(animal); }

Declare such constructor in Animal class and just pass a control to it from any subclass by using super(animal), where you will define your custom logic.

Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
  • It's a good idea and easy to implement. But infact I don't have any write permissions to these classes. Imagine I was called to add a function to the obvious sources which doesn't belongs to me. – jinge Apr 06 '16 at 13:41
  • 1
    So you want to do illegal downcasts on objects which classes you don't control. Sounds like a good plan /s. You are stuck with calling getters and setters then, enjoy. – Tobb Apr 06 '16 at 14:27
3

Ignoring the fact, that in your example you want to inverse the as-is relation (as in "an apple is-a fruit" vs "a fruit is-an apple") - which is pointless to a certain degree.

But lets imagine a case, where you have to make a potate look like an apple. So what you'll do is to carve out all the pulp and put the potato into the apple (I use the fruit example here because with animals it gets a bit messy). This is either called proxying or wrapping.

Both techniques can be done in java either statically or dynamically.

statically: a Wrapper:

  • Create new Class, i.e. VeggiAppleWrapper
  • let it extend Apple (so anything it wraps looks like an apple)
  • define a constructor that accepts the other type (i.e. Potato)
  • implement/override the methods that are common and delegate the calls to the wrapped object
  • implement all other methods throwing an UnsupportedOperationException

dynamically: a Proxy:

  • you have to define an interface at least for the target type (i.e. interface Apple)
  • create a proxy instance using Proxy
  • use the Apple interface in the list of implemented interface
  • implement a MethodInvocationHandler that does the forwarding for you, similar to the Wrapper, but using Reflection
  • cast the proxy to Apple

Alternatives to using Java's dynamic proxy are code generation libraries such as CGLib or Javaassist that generate a subclass of your target type at runtime, which is basically the same as the wrapper but implemented like the proxy.

In order to just copy the values from animal to the cat instance (as animal is a superclass, some fields should be common), you could use the reflection api as well

for(Field f : Animal.class.getDeclaredFields()) { 
  f.setAccessible(true)
  f.set(cat, f.get(animal));
} 

This is just a simple example, without exception handling and without considering the fields of Animal's superclass (getDeclaredFields only retrieves the field of this class).

Gerald Mücke
  • 10,724
  • 2
  • 50
  • 67
  • I'm afraid you misunderstand my opinion. Your solution is really good for delegating a class to another. But my question is more specific. Animal is Cat's superclass. I don't need to adapt any method of Cat, what I want is copying the common fields from Animal to Cat, while other unique fields of Cat remains default. – jinge Apr 06 '16 at 13:50
  • This works! It's just what I want. Reflection is a great technology haha. Thanks a lot. – jinge Apr 06 '16 at 14:44
-1

I think you do it right

Animal animal = new Animal();
Cat cat = new Cat();

public void foo(){
  animal = cat; //works because Animal is parent class
  cat = (Cat) animal; //you cast Animal to Cat like as parent to child
}

Check this out https://docs.oracle.com/javase/tutorial/java/IandI/subclasses.html

I hope it will helps

rob111
  • 1,249
  • 1
  • 13
  • 18
  • This does not answer what is being asked, of course you can cast a `Cat` object to fit a `Cat` variable, the OP wants to place a `Animal` object in a `Cat` variable. – Tobb Apr 06 '16 at 13:03
  • Sorry, my "a" is really an object of Animal, not an Animal reference of Cat object. – jinge Apr 06 '16 at 13:06
  • @jinge Is any of answer solving your problem? If not please provide more details of your problem. – rob111 Apr 06 '16 at 14:01
  • Thanks. One of the answers is really helpful, I am verifying whether it solve my problem. I will accept it or provide more details. – jinge Apr 06 '16 at 14:22