PDA

View Full Version : Redesigning my Variable class in Java


JeroMiya
2007.07.16, 11:22 AM
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:


/**
* 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.

ThemsAllTook
2007.07.16, 11:41 AM
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.

unknown
2007.07.16, 12:02 PM
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)

JeroMiya
2007.07.16, 12:29 PM
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

Fenris
2007.07.16, 12:33 PM
Java has this awesome operator called typeof.


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


(...and yes, I know C++ has this as well if RTTI is turned on.)

JeroMiya
2007.07.16, 01:21 PM
Yay! I can finally use this feature, since libraries can't turn it off in java.


I'm loving this language...

-JeroMiya

unknown
2007.07.16, 01:43 PM
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/

Josh
2007.07.16, 06:49 PM
And for one more option... ;)

if(a instanceof Int) {

OneSadCookie
2007.07.16, 08:04 PM
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...

AndyKorth
2007.07.17, 12:14 AM
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:
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.

Josh
2007.07.17, 12:32 AM
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:
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.

AndyKorth
2007.07.17, 12:37 AM
>.>

Oops... ahem... carry on here.

JeroMiya
2007.07.17, 09:13 AM
Yeah, I was kindof leading towards using the built-in Java Number classes, which should work fine for this.

-JeroMiya