I’m only a few weeks into my Java class and I’m already annoyed
at the language. I’m completely willing to ascribe this to newbieness,
where I’m just not working with what the language gives me, but the
metaobject stuff in Java seems a bit painful.
I’m working on a project for the class where I have to accept input in
several different units of heat (BTUs, calories, and joules) and output
the measurement in joules. I’ve made an abstract base class for the
various units and created concrete classes for each unit the program has
to read. I’d like to just have a list of available classes and have my
program enumerate them automatically (rather than hardcoding the behavior
for each), but the way I would normally think about doing this is painful
in Java.
In Delphi, I’d do something like this:
type
THeatUnits = class
public
constructor Create(Value : Real); virtual;
class function GetUnitsName : String; virtual; abstract;
function ConvertToJoules : Real; virtual; abstract;
end;
THeatUnitsClass = class of THeatUnits;
TBTUs = class(THeatUnits);
TCalories = class(THeatUnits);
TJoules = class(THeatUnits);
const
availableUnits : array [1..3] of THeatUnitsClass
= (TBTUs, TCalories, TJoules);
...
procedure DoStuff(Index : Integer; Value : Real);
var
Units : THeatUnits;
begin
Units := availableUnits[Index].Create(Value);
// Now do things polymorphically with Units.
end;
Delphi’s class types often seem like a quick hack to me, but they beat
what Java does in the same situation. For one thing, there doesn’t seem
to really be a class type in the same way that Delphi does it. There are
instead objects of type Class
. As far as I can tell, the best way to get
one of those is to call Class.forName("ClassName")
. But the painful
part is that there’s no specialization of class types at compile time, so
the Java code equivalent to my Units := availableUnits[Index].Create(Value);
above would be something like this:
static final AVAILABLE_UNITS = new String[] ("BTUs", "Calories", "Joules");
public void doStuff(int index, double value) {
Class unitClass = Class.forName(AVAILABLE_UNITS[index]);
Constructor unitConstructor = unitClass.getConstructor(new Class[] (double.class));
HeatUnits units = (HeatUnits)unitConstructor.newInstance(new Object[] (new Double(value)));
// now do things polymorphically with units.
}
(Common Lisp, of course, would be more succinct than Delphi, because
everything is first-class; I would probably do something like this:
(defclass heat-units () ())
(defgeneric get-units-name (unit-class))
(defgeneric convert-to-joules (unit-class))
(defclass btus (heat-units) ())
(defclass calories (heat-units) ())
(defclass joules (heat-units) ())
(defparameter +available-units+ #(btus calories joules))
(defun do-stuff (index value)
(let ((units (make-instance (svref +available-units+ index)
:value value)))
;; Now do things polymorphically with units.
))
)
As I said, though, I think this is just an artifact of not thinking in
Java to the appropriate degree. Most of Java’s reflection stuff seems set
up to be useful at run-time while Delphi’s run-time reflection is much
uglier than Java’s. And I think I’m going to approach my Java problem
from a different direction, with an enum and a factory method. I was
still struck by how annoyingly wordy (and not entirely typesafe) my first
approach turned out to be in Java.