Data Types
JFuncMachine supports the same native data types as the Java Virtual Machine:
- Boolean
- Byte
- Char
- Double
- Float
- Int
- Long
- Short
- Void (called Unit in JFuncMachine)
In addition, it has an Object type that contains a class name indicating the type of object, an Array type that contains a type indicating the type of array element, and
The Type class
The Type class in the package org.jfuncmachine.compiler.types
is the base class for all JFuncMachine types.
Simple Types
For types that represent
Java native types, and also for strings, JFuncMachine has a SimpleTypes
class that contains static fields for each type. These are:
public class SimpleTypes {
public static final Type BOOLEAN = new BooleanType();
public static final Type BYTE = new ByteType();
public static final Type CHAR = new CharType();
public static final Type DOUBLE = new DoubleType();
public static final Type FLOAT = new FloatType();
public static final Type INT = new IntType();
public static final Type LONG = new LongType();
public static final Type SHORT = new ShortType();
public static final Type STRING = new StringType();
public static final Type UNIT = new UnitType();
}You can use these static fields when specifying types or
comparing types. As long as you stick to the SimpleTypes class,
and never create instances of the simple types, you can use ==
to compare these types.
For example:
if (expr.getType() == SimpleTypes.INT) {Object Types
The ObjectType class represents the type of a Java object. It
has a string member variable indicating the fully-qualified (i.e.
including the package name) class name that the object is an
instance of. JFuncMachine can compile code to access classes that
are not in its classpath, which is why the class names are specified
with a string and not by java Class instances. This has good and bad
points. An advantage is that there may be fewer classpath issues,
but a disadvantage is that it will not do any compile-time checking
to see if two object types are compatible.
To create an instance of ObjectType for java.io.PrintStream:
Type psType = new ObjectType("java.io.PrintStream");Compound Types
Array Type
The ArrayType class represents the type of an array of some
object type. When you create an instance of ArrayType you
supply the type of the contained object. For example, to create
an ArrayType for an array of strings:
Type arrayType = new ArrayType(SimpleTypes.STRING);Function Type
JFuncMachine has support for lambdas and function references.
The FunctionType class represents the type of a function, containing
the types of each function parameter and the function’s return type.
For example, to create a FunctionType describing a function that
takes an int and a double and returns a String:
Type funcType = new FunctionType(
new Type[] { SimpleTypes.INT, SimpleTypes.DOUBLE}, SimpleTypes.STRING);Type Conversions
You may occasionally need to convert from one numeric type to another.
The org.jfuncmachine.model.expr.conv package contains expressions that
take an expression and convert its result to a different type.
This includes a conversion to the Unit type (a.k.a. Void), which really
means to just get rid of the value. For example, the following
expression creates an integer constant and converts it to a byte:
Expression byteValue = new ToByte(new IntConstant(5));The above expression is just for illustration. If you needed a byte
constant of 5 you could just do new ByteConstant(5).
JFuncMachine does not include any overflow checking for these conversions.
Boxing and Unboxing
Java sometimes needs to treat native types as objects, for example when storing
them in an ArrayList or HashMap. Each native type has a corresponding
Object type, such as java.lang.Integer for int types or
java.lang.Boolean for boolean types. Converting a native type to an
object type is called “boxing”, while converting from a numeric object type
to a native type is called “unboxing”.
JFuncMachine provides a Box and an Unbox class to either box or unbox
a value. In the simplest case, you can create a Box or Unbox expression
from another expression, and JFuncMachine will automatically determine
whether or not there is a boxed or unboxed equivalent for the given type.
For example, the following expression would result in an expression whose
type is java.lang.Integer (or in JFuncMachine representation, the type
new ObjectType("java.lang.Integer)):
Expression expr = new Box(new IntConstant(42));Sometimes you may want to explicitly specify the type of boxed or unboxed
value to generate. For example, you might have boxed Byte value that you
want to unbox as an int. You can provide the unboxed type to the Unbox
constructor to unbox a value as a particular type. For example:
Expression expr = new Unbox(new Box(new ByteConstant(42)), SimpleTypes.INT);You can do something similar for boxing values, where you supply the
type of the boxed value. In the following expression, we use the
getBoxType method of the Type class to get an object type representing
the boxed equivalent of the type:
Expression expr = new Box(new ByteConstant(42), SimpleTypes.INT.getBoxType());If there is no box type for the given type (e.g. String doesn’t need a box),
the getBoxType method returns null.
Autoboxing
Although boxing and unboxing can be a hassle, the Java compiler is pretty
good about boxing and unboxing automatically, a process that is called
“autoboxing”. JFuncMachine provides autoboxing on two levels. First, JFuncMachine
has autoboxing turned on by default, so it will insert Box or Unbox
expressions as necessary to try to get types to match up correctly.
You can also disable autoboxing by specifying different options for the code
generator. In that case, you may occasionaly need to make use of the
same autoboxing code that JFuncMachine uses, which is provided by the
Autobox class. The autobox method takes an expression and a desiret type
and tries to determine whether a box or unbox is necessary and if so, returns the
expression wrapped with the appropriate Box or Unbox expression.
You can also use the autoboxNeeded method to determine whether autoboxing
is necessary to convert an expression to a desired type.
A Note About Java Native Types
Although JFuncMachine masks a lot of the technicalities of the Java Virtual Machine, it is occasionally helpful to know how it does some things. One useful thing to know is that while the JVM has some support for boolean, byte, char, and short data types, those values usually have an int representation when the JVM is actually working with them. Now, an array of byte values is stored in memory as bytes, but whenever you fetch a byte value from an array of bytes, that value is actually an int.
The JVM essentially supports 5 types - int, long, float, double, and object. The only operations for byte, char, and short are to truncate an int to those values, and loading from and storing to arrays of those types.