Run time type ident (?) qn

There's a point I'm not quite understanding about run time type identification in Java...

I have attached a (very much cut down) program demonstrating what I'm trying to do, but basically the aim is this.  I have an abstract base class 'animal', whith two subclasses 'sheep' and 'wolf'.  Sheep and wolf each have a simple constructor (with just one parameter, though in my larger program I will have many parameters).  I want to have a 'reproduce()' method in my abstract base class (animal) so that in various other (unshown) routines of my program, I can pass an 'animal' object as a parameter and ask it to reproduce (so if the animal happens to be a sheep, I'll get a new sheep, and if it's a wolf I'll get a new wolf).

The version of the program I have attached currently doesn't work - I get an Instantiation exception.   I'm trying to use RTTI to accomplish the task (is there a better way?) and obviously my attempt at using the newInstance() method doesn't cut it - probably because the constructor for my objects expects a parameter (I tried a version using constructors with no parameters - it worked - however, I want to have parameters for my constructor, and hence somehow pass them from reproduce...)

Any thoughts?

Cheers

Michael

PS I'm using the Sun 1.2.2 JDK

/////////////////

// rtti.java
//
// Explore run time type identification in Java
//
// Michael Kerrisk, Jan 2000
//
// Class structure is as follows:
//
// animal
//      sheep
//      wolf
//

import java.util.*;

abstract class animal {
    String myName;

    static LinkedList allAnimals = new LinkedList();

    animal(String name) {
      myName = name;
      System.out.println(“New animal: ” + name);
      allAnimals.add(this);
    } // animal

    abstract public void sound();

    public void reproduce(String name) {
      Class c;

      System.out.println(“Animal “ + myName +
            “ producing offspring “ + name);
      System.out.println((this.getClass()).getName());

      c = this.getClass();

      try {
          c.newInstance();
      } catch (InstantiationException e) {
          System.out.println(“Instantiation exception”);
      } catch (IllegalAccessException e) {
          System.out.println(“IllegalAccess exception”);
      } // try
    } // reproduce
} // animal

class sheep extends animal {
    static LinkedList allSheep = new LinkedList();

    sheep(String name) {
      super(name);
      System.out.println(“New sheep: ” + name);
      allSheep.add(this);
    } // sheep

    public void sound() { System.out.println(“Baaaa”); }
} // sheep


class wolf extends animal {
    static LinkedList allWolves = new LinkedList();

    wolf(String name) {
      super(name);
      System.out.println(“New wolf: ” + name);
      allWolves.add(this);
    } // wolf

    public void sound() { System.out.println(“Growl”); }
} // wolf

public class rtti {
    public static void main(String[] arg) {
      sheep s;
      wolf w;

      s = new sheep(“Anne”);
      s.sound();
      s.reproduce(“Anne-Marie”);
    } // main
} // rtti

/////////////

>\jdk1.2.2\bin\java rtti
New animal: Anne
New sheep: Anne
Baaaa
Animal Anne producing offspring Anne-Marie
sheep
Instantiation exception


MiruAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

adam923Commented:
newInstance() needs a default (zero argument) constructor for that class. try adding one (and then you'd also need a setName(String n) method too)
0
MiruAuthor Commented:
Hi, thanks for the info.  I'd already seen that having a zero argument constructor would make this work.  My problem is that I want to have my objects reproduce themselves using a constructor *with* arguments.  Is there a way to do this?

Michael
0
adam923Commented:
yes, you need to use getConstructors() etc... here's an example that i tried (it works!)
//start of example file Test.java
import java.lang.reflect.*;

public class Test  {
 
  public static void main(String[] b)
    throws IllegalAccessException, InstantiationException,
         InvocationTargetException{
    Me c, a = new Me("first");
    a.clone("second");
    c = new Other("third");
    c.clone("fourth");
  }
 
} \

class Me{
  String name;
  public Me(String s){
    name = s;
    String type = getClass().getName();
    System.out.println("new "+type+" created with name "+name);
  }
  public Me clone(String s)
    throws IllegalAccessException, InstantiationException,
         InvocationTargetException{
    Object[] oa = new Object[1];
    oa[0] = s;
    return (Me)getClass().getConstructors()[0].newInstance(oa);
  }
}

class Other extends Me{
  public Other(String s){
    super(s);
  }
}

//end of example file
ok, now i'll expalin some of it...
notice the return line of the clone method has array index zero, this means that I assumed the target class only has one constructor and that this constructor takes a single String argument...
i think the rest should be self explainatory...
you'll probably want to catch the exceptions instead of throwing them again...
please ask if you have other questions
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Become a Certified Penetration Testing Engineer

This CPTE Certified Penetration Testing Engineer course covers everything you need to know about becoming a Certified Penetration Testing Engineer. Career Path: Professional roles include Ethical Hackers, Security Consultants, System Administrators, and Chief Security Officers.

adam923Commented:
disregard the rouge \ on the 14th line of code above... typo!
0
adam923Commented:
upon further research a more reliable method would be this:

public Me clone(String s)
  throws IllegalAccessException, InstantiationException,
             InvocationTargetException, ClassNotFoundException,
             NoSuchMethodException{
        Object[] oa = new Object[1];
        oa[0] = s;
        Class[] ca = new Class[1];
        ca[0] = Class.forName("java.lang.String");
        return (Me)getClass().getConstructor(ca).newInstance(oa);
    }

remember to either catch those exceptions or add those two other ones to the main method's throws clause.
0
mbormannCommented:
there is a better way of solving your problem ,solved by a Design Pattern known as 'Command' (Object behavorial pattern)
hence the name 'CommandInterface'

note you can remove the 'extends' from all your classes and
'program directly to the interface' as in the advice given by the seminal book 'Design Patterns : Elements of Reusable Software'

http://hillside.net/patterns/DPBook/DPBook.html

class Animal implements CommandInterface
{
      public Animal()
      {
            this.reproduce();
      }

      public Animal(String param1,String param2)
      {
            this.reproduce();
            this.dummyYOurOtherMethods(param1,param2);
      }
      
      public void reproduce()
      {
            System.out.println("Accepted a Animal with real class name as '"+this.getClass().getName()+"'");
      }
      
      public void dummyYOurOtherMethods(String param1,String param2)
      {
            System.out.println("In dummyYOurOtherMethods() with class name as '"+this.getClass().getName()+"'");
            System.out.println("In dummyYOurOtherMethods() param1= '"+param1+"'");
            System.out.println("In dummyYOurOtherMethods() param2= '"+param2+"'\n");
      }
}

class Wolf extends Animal implements CommandInterface
{
      public Wolf()
      {
            this.reproduce();
      }
      
      public Wolf(String param1,String param2)
      {
            this.reproduce();
            this.dummyYOurOtherMethods(param1,param2);
      }
      
      public void reproduce()
      {
            System.out.println("Accepted a Animal with real class name as '"+this.getClass().getName()+"'");
      }

      public void dummyYOurOtherMethods(String param1,String param2)
      {
            System.out.println("In dummyYOurOtherMethods() with class name as '"+this.getClass().getName()+"'");
            System.out.println("In dummyYOurOtherMethods() param1= '"+param1+"'");
            System.out.println("In dummyYOurOtherMethods() param2= '"+param2+"'\n");
      }
}

class Sheep extends Animal implements CommandInterface
{
      public Sheep()
      {
            this.reproduce();
      }

      public Sheep(String param1,String param2)
      {
            this.reproduce();
            this.dummyYOurOtherMethods(param1,param2);
      }

      public void reproduce()
      {
            System.out.println("Accepted a Animal with real class name as '"+this.getClass().getName()+"'");
      }
      
      public void dummyYOurOtherMethods(String param1,String param2)
      {
            System.out.println("In dummyYOurOtherMethods() with class name as '"+this.getClass().getName()+"'");
            System.out.println("In dummyYOurOtherMethods() param1= '"+param1+"'");
            System.out.println("In dummyYOurOtherMethods() param2= '"+param2+"'\n");
      }
}

interface CommandInterface
{
      public void reproduce();
}

public class test
{
      public static void main(String [] args)
      {
            test t =new test();
            /*
            One way of calling them
            
            t.dispatch(new Wolf("Wolfish1","Wolfish2"));
            t.dispatch(new Sheep("Sheepish1","Sheepish2"));
            t.dispatch(new Animal("Animation1","Animation2"));
            */
            
            //the direct way
            t.dispatch(new Wolf());
            t.dispatch(new Sheep());
            t.dispatch(new Animal());
      }
      
      public void dispatch(CommandInterface ci)
      {
            ci.reproduce();
      }
}
0
mbormannCommented:
sorry i cant do much follow up but i think it will accomplish ur task as it did mine.

I had 5 different methods like ur reproduce() called list() ,add() ,save() ,del() & apply() and right now am implementing the related classes.

hope many people turn to patterns.
it's a real treat working on Sunday's isn't it? Jod ,r u listening ?
:-)
0
adam923Commented:
how does this explore reflection, as was the authors purpose?
how does the clone (reproduce) method take parameters so that the cloned objects have different field values?
is their any real difference between implementing an interface and extending an abstract class with abstract methods? (besides having different type names)
0
JodCommented:
Another short answer coming up...

I might be around somewhere, though not at work...not yet anyway.

I was writing an example answer to this and my computer crashed so I don't have time to start from scratch again but here is the general idea. Even the general idea is ab it lengthy...


mbormanns example above uses parts of reflection and RTTI to perform late binding - the correct classes reproduce method is called at runtime from the despatch method because Java can identify the classes at runtime and so knows whether to call the animal reproduce method or the sheep one for example.

In Java abstract base classes are totally different to inetrfaces though they appear to perform the same function.

However, in Java you can only inherit from one class but you can implement many interfaces. Interfaces are the purest form of abstract class and have no implementation attached at all so any class can use them.

If you inherit from abstract base classes you limit your class heirarchy to one particular option, whereas Interfaces allow your class heirarchy to be dynamic. This goes in tandem with using object aggragation instead of object inheritance.

What does this mean? An example from one of my paq's...

*********************

The key trick is to keep a reference to another object and then delegate calls to it.

For example, a class window could inherit from a class called Rectangle and another class called UserInterfaceObject. This would be multiple inheritance implying that a Window IS a Rectangle and is ALSO a UserInterfaceObject.

However, what if you want to make a round window? You cannot have a Window that is both round and a rectangle so it is no longer correct to say that your Window inherits from Rectangle if it is round. You will have to change your class heirarchy so that it makes sense.

If you use delegation you avoid this by keeping a reference to a Rectangle object and also to a UserInterfaceObject. You then delegate Rectangle specific functions to the rectangle and UserInterfaceObject specific functions to the UserInterfaceObject.

This is called Object composition. In general it is ALWAYS possible to replace inheritance with object composition if you wish to.

In the above example it would work something like this:

public class Window {
  //set up window...
  boolean circularWindow = false;
  boolean rectangularWindow = false;
  Circle circle;
  Rectabgle rect;

  public void setWindowType (String windowType){
    if (windowType.equals("circle") {
      circularWindow = true;
      rectangularWindow = false;
      circle = new Circle(myradius);
    } else {
      circularWindow = false;
      rectangularWindow = true;
      rect = new Rectangle(mywidth, myheight);
    }
  }

  public int windowArea {
    int area = 0;
    if (circularWindow) {
      area = rectangle.getArea();
    } else if (rectangularWindow) {
      area = circle.getArea();
    }
    return area;
  }
}

This is a very simple example I have just written and there are better ways of doing this, but it shows how you can still inherit functionality without commiting your class heirarchy. In the above code you can even change your window from being a rectangle into a circle AFTER you have created it.

The counter side is that your code is a little more complex as you can see.

********************

There is an even neater way to do this with directly with interfaces but you get the point. This is why interfaces are better than solid inheritance as inheritance is very restricting once you have the ability to do RTTI.

(Trust me, there is a point somewhere in all of this - I'm just painting some background for you in case you are interested :)


What you are trying to achieve here is to have complete flexibillity to be able to instantiate any one of a number of classes that has constructors you do not want to set until runtime?

The best way to do this is to use something called a factory. A factory is a method that you recieves the details of what you want to create at runtime and tries to make an object of the required type. If it can't do so it will do something sensible instead.

For your example I would do something like this...

public class AnimalFactory {

  ....
  public static Animal create(String animalClass, Object[] parameters) {
    ....
  }
  ....
}

You then call it like this...

Sheep s = AnimalFactory.create("Sheep",new Object[] ("Sheep 1","Has Horns");

or something like that. Because it is a static method you never actually need to make instantiate an AnimalFactory class, you just use it.

Form this the Animal factory can identify (using RTTI) the class name you want to create and also using the following method:


public Constructor getConstructor(Class[] parameterTypes)
                           throws NoSuchMethodException,
                                  SecurityException

Returns a Constructor object that reflects the specified public constructor of the class represented by this Class object. The parameterTypes parameter is an array of Class objects that identify the constructor's formal parameter types, in declared order.
The constructor to reflect is the public constructor of the class represented by this Class object whose formal parameter types match those specified by parameterTypes.


to identify which constructor you want to invoke from the array list of parameters you have just passed in for this class.

The nice thing about abstracting this into a factory is that it can do clever things such as rather than just returning you an error but returning you the nearest matching constructor if it doesn't find an exact match for example.

Makes your code very adaptable and stable for using RTTI which is what you need.

Now you don't need an actual Animal class. Why? Because there is nothing for the animal class to do unless you actually want to create just an Animal but this does not really make sense as FUNCTIONALLY you only want to create sheep or wolfs. The Animal class is just a convenience you have to use to group them together and allow them to be treated as similar objects with common properties.

Where I am going, is that what you need is an Animal interface. Your sheep and wolf classes can them implement this interface as can any other class. EG:

public interface Animal {
//describe animal bits here
  public Animal reproduce(Object[] o) {}
}

public class Sheep implements Animal {
//implement animal bits...
  public Animal reproduce(Object[] o) {

    //How to make a new animal same as me but different...
    return AnimalFactory.create(this.CLASS.getName(), o);

  }
}

public class Wolf implements Animal {
//implement animal bits...
  public Animal reproduce(Object[] o) {

    //How to make a new animal same as me but different...
    return AnimalFactory.create(this.CLASS.getName(), o);

  }
}

You see how this works? When you want to reproduce an Animal of any type you call it's reproduce method and pass in the new parameters for the constructor:

  mySheep.reproduce(new Object[] ("new sheep","is fluffy");

In the reproduce method, the animal class knows what class name to pass on as it just uses it's own. After all you want to reproduce right so a sheep is not going to make a wolf.

It then passes on your constructor parameters to the animal factory and this returns you a new sheep and hey presto! We have RTTI and dynamic creation of Animals.

Your Sheep and wolf still share common properties (because they are both Animals as they both implement Animal) but can be instantiated at runtime using just strings.

Neat huh? Well that's the theory anyway...

0
adam923Commented:
Factories sound like fun but i still think Animal should be an abstract base class b/c it should have method bodies of common functionality to animals.
I still think (hope) I answered the actual question before... Miru?
0
mbormannCommented:
adam,
>>>>is their any real difference between implementing an interface and extending an abstract class with abstract methods? (besides having different type names)

Interfaces in Java are very powerful ,read up abt them in detail,they r wortj a very close second look.

Well .....,The Animal is the Interface having reproduce() ,I was having a hangover ....

Miru,
clarifications ?
0
JodCommented:
The thing is though, that in this case there is no common fumctionality between the sheep and the wolf class. They both reproduce but do it differently so you gain nothing except a load of unused code in Animal by retaining it as a base class.

If you wish to inherit functionality from another class it is often best to do so as I have shown in the Window example above. The reasons for this are long winded but if you are dealing with class objects dynamically at run time then Inheritance is nothing but a pain in the ***.

There are good reasons for this which are summarised in the book Design Patterns. Trust me - the book is a breakthrough in the way you think about class structures.

Also I would avoid using:

getConstuctors()

but use

getConstructor(Class[] parameterTypes)

instead. This way you get back the exact matching constructor for your class rather than an array of all constructors available.

You can use the Object array passsed into the factory to identify this matching constructor but making an array of class parameters from it.

EG:

Class[] ca = new Class[o.length];

for (int i = 0; i < ca.length; i++) {
  ca[i] = Class.forName(o[i].CLASS.getName())
}

Once you have an array of Class objects you can use this it Like this...

Constructor c = myClass.getConstructor(ca);

You now have the required constructor matching an array of parameters you have passed in at run time. To create your object do...

Object ob = c.newInstance(o);

o being your array of parameters you have passsed in.


Ultimately, this sort of code needs to be as flexible as possible and to do it without a factory will mean having to recode your classes every time you make a change and become more and more limiting as time goes by.
0
mbormannCommented:
continuing in the same line as above...
Design pattern 'Factory method' is defined in the GoF book as

define an interface for creating an object ,but let subclasses decide which class to instantiate. Also known as Virtual Constructor.

from another text James Cooper's 'Design Patterns Java Companion'

There are several similar variations on the factory pattern to
recognize.
1. The base class is abstract and the pattern must return a complete working class.
2. The base class contains default methods and is only subclassed for cases
where the default methods are insufficient.
3. Parameters are passed to the factory telling it which of several class types to return. In this case the classes may share the same method names but may do something quite different.
0
adam923Commented:
mbormann and jod- i wonder if besides just preaching longwindedly about how you'd like to design a program if you could actually read what miru's code was doing?  you say animal should be an interface but obviously if you look at it he's keeping a static field in animal with references to all animals and providing other shared functionality.  also, you complain that i use getConstructors instead of getConstructor but i actually did correct this if you'd take the time to read the comments instead of just posting your own.
i find both of your suggestions rather ridiculous here... miru obviously wants to experiment with reflection (read the header comments).  you're taking a simple question of "how can i do this?" and answering with "go back to the drawing board" where this is unneccesary and defeats the original purpose!
0
JodCommented:
To be honest adam923, I was actually answering your questions above :) and also pointing out that if Miru wants to develop this program into a much larger one (as Miru says above) then there will be problems further down the line.

EG:
>> is their any real difference between implementing an interface and extending an abstract class with abstract methods? (besides having different type names)
>> i still think Animal should be an abstract base class b/c it should have method bodies of common functionality to animals.

As they say, don't shoot the meesenger - you did ask...

Just quickly, the reference to all animals created would be kept in the AnimalFactory because it makes more sense to keep it here logically and functionally rather than each animal incorporating an Array of all animals.

Sorry my computer kept crashing when I posted the comment so I didn't read this properly:

public Me clone(String s)
  throws IllegalAccessException, InstantiationException,
             InvocationTargetException, ClassNotFoundException,
             NoSuchMethodException{
        Object[] oa = new Object[1];
        oa[0] = s;
        Class[] ca = new Class[1];
        ca[0] = Class.forName("java.lang.String");
        return (Me)getClass().getConstructor(ca).newInstance(oa);
    }

I see you changed to find the correct constructor using a parameter but the parameter is still hardcoded at compile time as opposed to being dynamic.

The problem is, and please don't take this the wrong way, but you may as well use the first answer because the clone method here can still only deal with a set number of parameters of a type specified at compile time.

What if you want to create an Animal with a constructor that takes a parameter other than a string?

As you create more diverse Animals and classes this way of cloning will start to be come a bit limiting (at least in the context of exploring RTTI).


To really use RTTI nothing is decided until runtime so you would get something more like this...

public Me clone(Object oo)
  throws IllegalAccessException, InstantiationException,
             InvocationTargetException, ClassNotFoundException,
             NoSuchMethodException{
        Object[] oa = new Object[1];
        oa[0] = oo;
        Class[] ca = new Class[1];
        ca[0] = oo.getClass().getName();
        return (Me)getClass().getConstructor(ca).newInstance(oa);
    }

You can see how this starts to get a bit more flexible? You can now have constructors with numbers or arrays or any other object.

Ultimately though, as you make this code more and more flexible it tends towards becoming a factory.


The reference to all animals created would be kept in the AnimalFactory because it makes more sense to keep it here logically and functionally.
0
MiruAuthor Commented:
Hi everyone.  Thanks for all the information.  I've just been away a couple of days, and didn't expect to get so much response!  I will have to have a look at all the responses above - a little daunting as some of it is at the edge of my Java knowledge.  Back soonanyway.

Cheers

Michael
0
mbormannCommented:
>>>>>a little daunting as some of it is at the edge of my Java knowledge

that's the only real fast way u learn ,when there's a chance that u might drown in the case when u have been thrown into the ocean.
Maybe I am a bit long winded abt it ,I have taken up after Jod,what say matey?
0
mbormannCommented:
Miru,
If u dont invite some person to lock it then these questions get auto deleted after a gap of 20 days if there is no activity.better make it a PAQ.
0
adam923Commented:
yes, please evaluate the responses and give us feedback
0
MiruAuthor Commented:
Sorry so long to respond - I've been away a lot recently, and there is a fair bit of new stuff for me to understand here.  I have had a bit of a look at this stuff now.  I first of all want to day thanks for the incredible detail in the response from everyone.  I think the answer from adam123 was probably closest to what I wanteed to do, though in the light of all the info I got I've changed things in general a bit.

Is there a way that I can give points to more than one respondee, since I feel that answers from mbormann and Jod have also been really useful.
0
adam923Commented:
you can delete this question and then post new questions worth x points that just say "for mbormann" in the title
0
mbormannCommented:
>>>>>Is there a way that I can give points to more than one respondee

dont delete question if u have points to spare,invite somebody to lock it and post new question(s) for others.

But if u r short of points then delete this and post a question 'for XXX' and pls mention for comment at 'this' particular question.

I have a textbook in online format on Design Patterns,if u or adam923 want thempls mail me at ID given in my profile ,also please take a look at these good URL's.

From
http://www.experts-exchange.com/jsp/qShow.jsp?ta=java&qid=10264299 

Excerpts from Design Patterns
                http://www.ti.et-inf.uni-siegen.de/Entwurfsmuster/ArtikelImport/osefa/patterns/patterns.htm

Arthur Riel's OO Design Heuristics
http://www.cse.unsw.edu.au/~timm/pub/subjects/oois96/rules.html

General Design Patterns
http://www.eli.sdsu.edu/courses/spring98/cs635/notes/index.html
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Java

From novice to tech pro — start learning today.