Link to home
Start Free TrialLog in
Avatar of edwardiii
edwardiii

asked on

Looking for way to implement a method for a class after I've obtained the class name as a String.

Hello, all.  This is my first Java question (am transitioning from VB to Java).

Simply put, if class A has method howdy, can I have A running, implement class B, and from B return a value to A's howdy method other than with "A.howdy(returnValueGoesHere)"--by using a string "A" that I know represents the A class?  Below is a more complete description of my problem:

I have a "BlackBerry" Class, which requires users to enter various data.  It has a "Select Date" JButton that invokes another class named "MyCalendar".  I've set up the calendar so that when a user clicks a date, the string value (dateChosen) is sent to the (still running) BlackBerry instance via BlackBerry's "displayMessage" method, and is populated in the "Date" JTextField.  The code that does this is:

             BlackBerry.displayMessage(dateChosen);

I have about 20 different classes that all open the same MyCalendar application (e.g. BlackBerry, Treo, etc.).  I know using a selection structure to grab the class name that instantiated MyCalendar is wasteful, so I'm trying to grab the name of the class that called MyCalendar (this is a string, I think) and use it to run the displayMessage method (which all 20 classes contain).

With this I can grab the name of the instantiating class:

             variable = callingClass.getClass().getName()

Is there any way to use the variable to actually run the displayMessage method?  

These don't work:

             callingClass.getClass().getName().displayMessage(dateChosen);
             variable.displayMessage(dateChosen);

I read several threads here at EE with suggestions regarding using forName, as below, but they create new instances of the class I want to return a value to, and fail to return the dateChosen value to the class (BlackBerry, Treo, etc.) that originally called MyCalendar(and is still running while waiting for the user to select a date):

              Class findClass = Class.forName("BlackBerry");
              BlackBerry findIt = (BlackBerry)findClass.newInstance();              
              findIt.displayMessage(dateChosen);
Avatar of topher1120
topher1120

Hi Edwardiii,
I think your interconnectivity is a little fuzzy.  
Typically, what you want to do in your ActionListener class is call the MyCalendar object and get the Date or String object from MyCalendar and pass it to your BlackBerry class.  For example, you can write your ActionListener class like so:

public class MyBBActionListener implements ActionListener{
      private BlackBerry bb = null;
      private MyCalendar cal = null;

      public MyBBActionListener(BlackBerry b, MyCalendar c){
           this.bb = b;
           this.cal = c;
      }

      public void actionPerformed(ActionEvent ae){
            String date = cal.getSelectedDate();
            bb.displayMessage(d);
      }
}

then MyCalendar would simply be used only to display the calendar and return the appropriate date.

public MyCalendar{

      public MyCalendar(){
          // do initialization stuff here, but don't display calendar.
      }

      public String getSelectedDate(){
           // display calendar, get the date selected by the user, return date as a string.
      }

}

public BlackBerry{
     
     public BlackBerry(){
         // do initialization stuff.

         JButton selDate = new JButton("Select Date");

         selDate.addActionListener(new MyActionListener(this, new MyCalendar() ));

         // add button to frame and to other init stuff.
     }

}

This way, your calendar object never needs to know what object is calling it.  It only needs to display the calendar and get the users input.  Likewise, the BlackBerry object never needs to know where it actually gets the date from.  Only the action listener needs to know the different pieces.

Thanks,
C
Avatar of edwardiii

ASKER

Thanks, Topher1120.

Fuzzy is an understatement; coming from years of Visual Basic experience, Java is quite grueling to me:)  Below is code I'm using to transfer dateChosen from the MyCalendar class to the BlackBerry class:

public class BlackBerry extends javax.swing.JFrame

//variable declarations
private static String displayMessageResults;

     public static void displayDate(String dateChosen)
     {
          displayMessageResults = dateChosen;  //sets displayMessageResults to the value obtained for dateChosen from the MyCalendar class
          stopDateJTextField.setText(displayMessageResults);  //populates BlackBerry's stopDateJTextField with the results from MyCalendar
     }

Inside MyCalendar, there is an ActionListener set to monitor all the date buttons.  When a button is clicked, the string "dateChosen" is fed that calendar date value.  Then I do the following to get the results back to the BlackBerry class:

BlackBerry.displayDate(dateChosen);

Now here's where I need your help.  In my BlackBerry class, I have some ActionListeners set up inside the Select Date's JButtonActionPerformed routine.  For example, to react to a user clicking the "Today" button in MyCalendar, I have:

newMyCalendar.todayJButton.addActionListener(
     new ActionListener()
     {
          public void actionPerformed (ActionEvent event)
          {
               myFrame.dispose();  //tears down the Calendar so user sees a re-enabled BlackBerry instance
               show()  //have to do this to get the running BlackBerry instance to have the focus, otherwise the user has to click the form
               lastMoJTextField.requestFocus();   //set the focus in the next field the user needs to complete
          }
     });

The above only deals with getting rid of the MyCalendar instance and setting focus back to BlackBerry.  Is there some way to embed the equivalent of my "public static void displayDate..." inside of the ActionListener above?  How exactly does one "get the date selected by the user, return date as string", without entering something like BlackBerry.displayDate(dateChosen) as I currently have in MyCalendar?

I tried to follow your example, but am unsure of how to treat MyBBActionListener.  It that a class inside BlackBerry?  A stand-alone class that all my applications will interact with to get MyCalendar results (like an interface)?  Either way, code such as:
      private BlackBerry bb = null;
      private MyCalendar cal = null;

      public MyBBActionListener(BlackBerry b, MyCalendar c){
           this.bb = b;
           this.cal = c;

leads me to believe I've got to set up similar variables for every app I've got, such as Treo, iPaq, etc.  I am trying to avoid having to input all the class names calling MyCalendar, whether in a selection statement in MyCalendar(which I can do and it works), or in MyBBActionListener, or anywhere else.  Thanks again for your time:)
ASKER CERTIFIED SOLUTION
Avatar of topher1120
topher1120

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Great solution, topher1120:)  I only had to make minor edits (see below).  I'm awarding you top grade/full points; your method saved me some coding and works perfectly.  As I said, I'm new to Java, so I've got to ask this:  Is it possible that a situation could arise where a programmer would need to fire a method by using a string name for the method's parent class (as per my original question)?  Is the firePropertyChange method something that obviates the need for such a solution in all cases?  If not, why is it that "callingClass.getClass().getName().displayMessage(dateChosen);" can't be used to fire the displayMessage method of an external class?



In MyCalendar, in the firePropertyChange I used dateChosen instead of selectedDate, as follows:

     firePropertyChange("selectedDate",null,dateChosen);

In BlackBerry's addPropertyChangeListener I had to add a "new PropertyChangeListener()" line after the "newTemplateCal.addPropertyChangeListener, as follows:


     newMyCalendar.addPropertyChangeListener(
         new PropertyChangeListener()
         {

             public void propertyChange(PropertyChangeEvent evt)
             {
                 if(evt.getPropertyName().equals("selectedDate") )
                 {
                    myFrame.dispose();  
                    show();  
                    lastMoJTextField.requestFocus();  
                    BlackBerry.displayMessage((String)evt.getNewValue());
                 }
             }
     });
Hi edwardiii,
I'm not sure I quite understand your questions, but I'll take a stab at this anyway.  What I think you are asking is: 1) Is there a time where you don't know the calling class name, but know the method name you want to call and how do you do it?  Then, 2) how does the PropertyListener, ActionListener, etc fit into separation of logic?

1)  There are really two ways you will typically see this done.  The first is the use of inheritance, which I strongly advise a good look at.  The second is the Java Reflection API, which is more complex, and not overly easy to understand at first.
 I have found some items in Sun's Java Tutorial that should help with understanding inheritance and and book covering standard Java programming will address this as well.  
http://java.sun.com/docs/books/tutorial/java/index.html
Inheritance and method overriding are where object oriented progamming really get their power.  Basically, what this boils down to is that you can create a "super class" that some of your classes can "extend" or "inherit."  These super-classes can define common functionality between a group of classes.  For example, if you really wanted MyCalendar to be able to directly call BlackBerry.displayMessage(String val), (not recommended, use the PropertyChangeListener approach) but have it work for Treo and IPaq, then you could create a super class named WirelessDevice.  WirelessDevice could then define displayMessage(String val).  Then BlackBerry, Treo, IPaq would all extend WirelessDevice.

public class WirelessDevice{
     
    public void displayMessage(String val){
        // do standard stuff here.
    }
}

public class BlackBerry extends WirelessDevice{

    public void displayMessage(String val){
        // method overriding is good, because MyCalendar will think it is  calling WirelessDevice.displayMessage, but
       // BlackBerry.displayMessage will actually be called.
    }

}

Using reflection is more difficult, like I said and would not advise it unless you are instantiating classes at runtime that you don't know at compile time (reading a class name from a text file or similar.)  Even then, a hybrid approach of using reflection and inheritance is the best way to go (require the class name in the text file to be a sub class of a specified super class, then act as above.)  I will not go further into this, but just to show you why "callingClass.getClass().getName().displayMessage(dateChosen);" does not work, we can break this down.
getClass() function returns a class of type java.lang.Class.  Class is part of the reflection api, even though it does not reside in java.lang.reflect package.  getName() in java.lang.Class returns a String with the name of the class type. For example, it would return "com.mycompany.mypackage.BlackBerry" as a String.  Since the String class does not have a method called displayMessage(String), this would not work.

2)  In an ideal situation, your BlackBerry program doesn't care where it gets the date from, so long as it is in a format it accepts.  You may want to use something other than MyCalendar in the future to provide the date to BlackBerry and if you tied MyCalendar function calls in BlackBerry, you would have to reprogram BlackBerry to change this.  Similarily, you should want MyCalendar to be autonomous, it doesn't care who is receiving information from it.  It just displays the calendar and does some standard notification of changes, allowing for Anything to use it.  You also don't want changes in how MyCalendar works to affect how BlackBerry works (new method calls, etc).  Listeners provide a way for each to stay seperate, yet provide communication between the two.  In our discussion, we created the PropertyChangeListener as an anonymous inner class to BlackBerry.  If we really didn't want to change anything in BlackBerry when we changed out the calendar, we could have created the listener as a separate class and (hopefully) only that separate class would need to change.  Ideally, only the listener really knows about the interaction between the two classes.  One other side-effect of this is: a) you could have multiple date input sources for the BlackBerry. b)Multiple applications/views could listen to the same calendar and change accordingly, not just the BlackBerry.

Hope this helps,
C
Thanks, C(hristopher?).

Another great explanation.  I'm deriving from your comments there would never be a need to do what I intially requested that could not be handled by custom superclasses/and/or Listeners.  Thanks again:)
Yes, I think the chances that you would find a situation that would call for a full usage of the Reflection API would be extremely rare.  If you get into a position where you are making heavy use of the api, you should take a step back and really look at what you are trying to do.

Good luck and Happy New Year.

C