Redesigning my Variable class in Java

Member
Posts: 45
Joined: 2006.11
Post: #1
As an exercise in learning java, I'm porting and rewriting/redesigning some of my old code from C++ to Java. I came across my Variable class (used in the implementation of a scripting language for the greenengine project) and it is in need of a redesign.

First, here is the original class:

Code:
/**
* This file contains the Variable class, which represents a generic
*  variable in a game Entity. Variable can be an integer or a float.
*  More variable types may be added in the future
*
* \file Variable.h
* \author Jeremy Bell
* \date 02-26-07
*/

#ifndef VARIABLE_H
#define VARIABLE_H

#include <iostream>
#include <boost/shared_ptr.hpp>

namespace green
{

    /**
     * This is the Variable class, which is used by the Entity class
     *  to represent custom object variables. For now, variables can
     *  either be integers or floating point values.
     */
    class Variable
    {
    public:
        /// This represents the type of variable it is
        enum Type {FLOAT_VAR, INT_VAR};
        
        /** This union represents the value of the variable
         *   I only recommend adding built-in types here, and possibly one
         *   string type in the future.
         */
        union Value {
            Value() : intVal(0) {}
            Value(int ival) : intVal(ival) {}
            Value(float fval) : floatVal(fval) {}
            int intVal; ///< the integer value of the variable if it is an int
            float floatVal; ///< the float value of the variable if it is a float
        };
        
        /** Variables default to INT_VAR types */
        Variable() : mType(INT_VAR) {mValue.intVal = 0;}
        
        /** Copy constructor from an int */
        Variable(int copy) {
            mType = INT_VAR;
            mValue.intVal = copy;
        }
        
        /** Copy constructor from a float */
        Variable(float copy) {
            mType = FLOAT_VAR;
            mValue.floatVal = copy;
        }

        /** Overloaded the == operator between Variable objects.
         *  \param rhs This is the right hand side of the == operation.
         *  \return Returns true if the two Variable objects are the same type
         *    and have the same value.
         */
        bool operator==(const Variable& rhs) const
        {
            bool ret = false;
            if(rhs.mType == mType)
            {
                switch(mType)
                {
                    case INT_VAR:
                        if(mValue.intVal == rhs.mValue.intVal)
                            ret = true;
                        break;
                    case FLOAT_VAR:
                        if(mValue.floatVal == rhs.mValue.floatVal)
                            ret = true;
                        break;
                }
            }
            return ret;
        }
        
        /** Assignment to an integer. Sets the type to INT_VAR and the value
         *  appropriately.
         *  \param value The new value of the Variable, as an integer.
         *  \return returns reference to *this, as per convention of = operator
         */
        Variable& operator=(int value)
        {
            mType = INT_VAR;
            mValue.intVal = value;
            return *this;
        }
        
        /** Assignment to an float. Sets the type to FLOAT_VAR and the value
         *  appropriately.
         *  \param value The new value of the Variable, as a float.
         *  \return returns reference to *this, as per convention of = operator
         */
        Variable& operator=(float value)
        {
            mType = FLOAT_VAR;
            mValue.floatVal = value;
            return *this;
        }

        /** Print the Variable to a std::ostream. Also prints the type of the
         *  Variable for easy reading. May provide a bare print method in the
         *  future if necessary.
         *  \param os The output stream to print the variable to.
         */
        void Print(std::ostream& os) const
        {
//            os << "Type: ";
//            switch(mType)
//            {
//                case INT_VAR:
//                    os << "INT_VAR VALUE: " << mValue.intVal;
//                    break;
//                case FLOAT_VAR:
//                    os << "FLOAT_VAR VALUE: " << mValue.floatVal;
//                    break;
//            }            
        }
        
        /** Default constructor defined as virtual */
        virtual ~Variable() {}

        /** Get the type of variable it is */
        inline Variable::Type GetType() const { return mType; }

        /** Set the type of variable this is
          * \param type The new type of variable
          */
        inline void SetType(Variable::Type type) { mType = type; }

        /** Get the value of the variable */
        inline Variable::Value GetValue() const { return mValue; }

        /** Set the value of the variable
          * \param value The new value of the variable
          */
        inline void SetValue(Variable::Value value) {mValue = value; }
        
        /** Set the value of the variable to an int
         * \param value The new value of the variable
         */
         inline void SetValue(int value) { *this = value; }
        
        /** Set the value of the variable to a float
         * \param value The new value of the variable
         */
         inline void SetValue(float value) { *this = value; }
        
    private:
        Variable::Value mValue; ///< the value of the variable
        Variable::Type mType; ///< the type of the variable
    };
    typedef boost::shared_ptr<Variable> VariablePtr;
    
    /** Overload the << operator for std::ostream and Variable. Uses
     *  Variable::Print internally.
     *  \param lhs The output stream to Print to.
     *  \param rhs The Variable to Print.
     */
    inline
    std::ostream& operator<<(std::ostream& lhs, const Variable& rhs)
    {
        rhs.Print(lhs);
        return lhs;
    }
    
    /** Overload the << operator for std::ostream and Variable::Type
     * \param lhs The output stream to print to.
     * \param rhs The Variable::Type to print
     */
    inline
    std::ostream& operator<<(std::ostream& lhs, const Variable::Type& rhs)
    {
        switch(rhs)
        {
            case Variable::INT_VAR:
                lhs << "INT_VAR";
                break;
            case Variable::FLOAT_VAR:
                lhs << "FLOAT_VAR";
                break;
            default:
                lhs << "Unknown Variable::Type";
                break;
        }
        lhs << std::flush;
        return lhs;
    }

    /** Overload the << operator for std::ostream and Variable::Value
     * \param lhs The output stream to print to.
     * \param rhs The Variable::Value to print
     */
    inline
            std::ostream& operator<<(std::ostream& lhs, const Variable::Value& rhs)
    {
        lhs << "(" << rhs.intVal << ", " << rhs.floatVal << ")" << std::flush;
        return lhs;
    }
    
    /** Print a Variable::Value based on a Variable::Type
     * \param value the value to print
     * \param type the type of value to print
     * \param os The ostream to print to
     */
    inline
    void PrintValue(const Variable::Value& value, Variable::Type type, std::ostream& os)
    {
        switch(type)
        {
            case Variable::INT_VAR:
                os << value.intVal;
                break;
            case Variable::FLOAT_VAR:
                os << value.floatVal;
                break;
            default:
                os << "unknown Variable::Type" << std::endl;
                break;
        }
    }
}
#endif

Now, as you can see, it uses a union type to store the value of the variable, and an enum type to store the type of the variable (in this case, just INT or FLOAT). This design has been somewhat problematic elsewhere in the project.

Does anyone have any suggestions for redesigning this class in Java?
-JeroMiya

edit: Don't ask why I'm implementing my own scripting language instead of using an existing one. This project is a class project. I was told to do it this way.
Quote this message in a reply
Moderator
Posts: 1,560
Joined: 2003.10
Post: #2
JeroMiya Wrote:Now, as you can see, it uses a union type to store the value of the variable, and an enum type to store the type of the variable (in this case, just INT or FLOAT). This design has been somewhat problematic elsewhere in the project.
I implemented something like this long ago. My first approach was basically the same as yours. Later, I changed it to use void * instead of a union, and typecasted based on the enum value. Neither is really ideal, but I think this is conceptually about the best you can do with this problem...

Not sure how that translates to Java, though. Sorry.
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #3
just have (with appropriate/allowed names)
class Float extends Variable,
class Int extends Variable...
then your Variable type can be either a Float or an Int.

(you can make Variable an abstract class or interface as well so that instances of it are not made)

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Member
Posts: 45
Joined: 2006.11
Post: #4
unknown Wrote:just have (with appropriate/allowed names)
class Float extends Variable,
class Int extends Variable...
then your Variable type can be either a Float or an Int.

(you can make Variable an abstract class or interface as well so that instances of it are not made)

How do I check the type of a Variable after it has been instantiated as a subclass/implementation? Sorry if I'm a newbie of Java. Thanks for your help.

-JeroMiya
Quote this message in a reply
Oldtimer
Posts: 834
Joined: 2002.09
Post: #5
Java has this awesome operator called typeof.

Code:
if (typeof(myVar) == "Float")

(...and yes, I know C++ has this as well if RTTI is turned on.)
Quote this message in a reply
Member
Posts: 45
Joined: 2006.11
Post: #6
Yay! I can finally use this feature, since libraries can't turn it off in java.


I'm loving this language...

-JeroMiya
Quote this message in a reply
Sage
Posts: 1,403
Joined: 2005.07
Post: #7
if (a.getClass() == Int.class) {

the java browser is really helpful for looking up what functions a class has and such
comes with the developer tools, check /Developer/Applications/Java Tools/JavaBrowser.app/

Sir, e^iπ + 1 = 0, hence God exists; reply!
Quote this message in a reply
Moderator
Posts: 608
Joined: 2002.04
Post: #8
And for one more option... Wink

if(a instanceof Int) {
Quote this message in a reply
Luminary
Posts: 5,143
Joined: 2002.04
Post: #9
I've never heard of "typeof" and checking for equality of classes fails under inheritance. instanceof() is the "right" way to do this check, but is usually a sign that you need another virtual method...
Quote this message in a reply
Moderator
Posts: 452
Joined: 2008.04
Post: #10
I don't mean to be picky, but typeof is not a keyword in Java.

I would suggest using the instanceof keyword, since that's what it's there for, in which case you'd do:
Code:
if(a instanceof Integer) //Int won't work here


You might be interested in using the Number class. Both Integer, Float, Long, Short, and Double are all Numbers. This way, you can use the existing Java number classes without having to define your own with a supertype of your own. I think it should take care of most of what you'd want to do.
Quote this message in a reply
Moderator
Posts: 608
Joined: 2002.04
Post: #11
AndyKorth Wrote:I don't mean to be picky, but typeof is not a keyword in Java.

I would suggest using the instanceof keyword, since that's what it's there for, in which case you'd do:
Code:
if(a instanceof Integer) //Int won't work here


You might be interested in using the Number class. Both Integer, Float, Long, Short, and Double are all Numbers. This way, you can use the existing Java number classes without having to define your own with a supertype of your own. I think it should take care of most of what you'd want to do.
Well yeah. Int is his custom class.
Quote this message in a reply
Moderator
Posts: 452
Joined: 2008.04
Post: #12
>.>

Oops... ahem... carry on here.
Quote this message in a reply
Member
Posts: 45
Joined: 2006.11
Post: #13
Yeah, I was kindof leading towards using the built-in Java Number classes, which should work fine for this.

-JeroMiya
Quote this message in a reply
Post Reply 

Possibly Related Threads...
Thread: Author Replies: Views: Last Post
  Java just making a class in Netbeans!? darkownagepeace 1 2,045 Jul 21, 2007 02:22 PM
Last Post: wyrmmage