Java: Long.TYPE vs Long.class
Here's something I came across yesterday which is mildly annoying (I figure this problem will disappear in Java 1.5 with auto-boxing).
I'm using reflection (appropriately, I feel :)) in my code and in a particular case I'm trying to invoke a constructor with two (small el) long parameters. When debugging the code I discovered that this constructor is not equivalent to one with two (big el) Long parameters. This seems kind of obvious, but I'd always regarded Long as a wrapper for long and assumed that when using reflection so did the JVM (i.e. conversion Long <-> long was handled tranparently; thinking about this more I can see why it isn't: the method x( long ) != the method x( Long ) ).
The way I determine the parameter type involves pulling an Object out of a Collection and calling getClass() on it - therefore meaning I'm going to get an Object wrapper for any primitive type. After some Googling I discovered the Long.TYPE (and then paid some more attention to it in the JavaDoc :)) which is effectively (small el) long.class (Long.class != long.class).
In the context of my problem I have a bad feeling that I'm going to have to provide alternative constructors with (big el) Long parameters wrapping the original constructors (in lots of [well, at least some :)] places) and I don't really want to (bad code smell).
Hopefully the new day will provide a fresh perspective, but in anticipation of spending the day banging my head on the desk in frustration - has anyone come across this problem and dealt with it successfully?
Thanks.
Walter Rumsby
Monday, December 29, 2003
What are your thoughts on this code for your case?
public class A {
public A(long p1, long p2) {
this(new Long(p1), new Long(p2));
}
public A(Long p1, Long p2) {
// some code
}
}
Scot Doyle
Monday, December 29, 2003
That's what I'm thinking I need to do, but I'm a little resistant to the idea. I'd rather make changes to my code than make modifications to other code because I could miss some code I need to modify.
Walter Rumsby
Monday, December 29, 2003
Long is indeed a wrapper for long. However the JVM will *never* do automatic conversion between the two cases.
The example code above is the kind of thing you'll need to do. Where I work we keep Long, Integer etc. as lightly used as possible for just this kind of reason.
BigDecimal is another one to consider.
Michael Koziarski
Monday, December 29, 2003
Silly question then, why are those two constructors doing different things? In other words, shouldn't they be interchangeable? Are they in your code or someone else's?
Scot Doyle
Monday, December 29, 2003
I'm not sure I understand your question Scot. It was directed at me?
It would be nice if the class long.class (Long.TYPE) had a field "value" or something, but I can't see any when I reflect into a newInstance of the Class.
Hmm, looks like I'll be implementing alternative constructors then :)
Walter Rumsby
Monday, December 29, 2003
> has anyone come across this problem and dealt with it successfully?
It's not exactly clear to me what the problem is. You want to call a function that takes different types than the ones you have? There's no option but to convert. As another poster said, Java rarely (compared to C++) does implicit conversions, and never on the types you're dealing with.
What's the big picture?
Brian
Monday, December 29, 2003
I'm using reflection since I have to determine the parameters for the constructor at run-time. The problem is that I'm determining the Class of the parameters by looking at the Class of an Object returned by another method.
Since that method returns an Object it is wrapping long instances in Long. Long.class != long.class, so I can't call a constructor MyAwesomeObject( long, long ) with two Longs (NoSuchMethodException/NoSuchConstructorException), but I can't return a long from a method that returns Object.
The solution here seems to be create another constructor MyAwesomeObject( Long, Long ) that invokes the primitive constructor.
A bit of a pain. I'd like some syntax sugar on that please.
Walter Rumsby
Monday, December 29, 2003
I have had this problem in the past. The only solution that does exactly what you are requesting (that I am aware of) is to brute force it... try all combinations of the arguments that have corresponding primitive types in both their primitive and object format. My conclusion was that the reflection mechanism leaves something to be desired :-)
Scot Doyle
Monday, December 29, 2003
Methinks the problem lies with a design that depends on reflection to call a constructor. It's factory time!
T. Norman
Monday, December 29, 2003
To be honest, it sounds like you're making this a lot harder than it needs to be; I don't even understand what the "problem" is here. What's so bad about creating an overloaded constructor?
Based on your description of the code, it sounds like you simply need to do the following (only one paramter to simplify the discussion):
Object unknownObj = ...;
MyAwesomeClass foo = null;
if(unknownObj != null && unknownObj instanceof Long)
{
Long l = (Long) unknownObj;
foo = new MyAwesomeClass(l.longValue());
// or invoke constructor via reflection
}
Burninator
Monday, December 29, 2003
Well the problem is a little more complex than what I've outlined. I tried to focus on what was the crux of the problem (in my mind).
Basically I'm working with a lot of existing code relying on a persistence layer that is not... optimal (in design or implementation - yes, I'd love to tear it down etc etc, but that's not really feasible right now). The part I'm working on imports data from a text file and then checks to see if that data element already exists in the "persistence layer" (i.e. alternative implementations of the interface I wrote go straight to the database or ask the persistence layer which should have the relevant items cached for better performance) based on primary keys.
Nice and simple if (as in most cases) the primary key is a long/Long, but in some cases it is a composite key and I have to create an instance of the ???PrimaryKey Class. I have to take what the existing API and files throw at me which is why I prefer to make fewer changes to other people's code (having spent 3-4 hours adding overloaded constructors and updating toString(), equals() and hashCode() implementations for Classes representing composite primary keys I'm not so keen to repeat the process).
Reflection seemed appropriate and seems to work quickly enough but could easily not be the best solution here.
Walter Rumsby
Monday, December 29, 2003
Aha! So you're dealing with a transparent persistence layer!
So much effort is placed into building and working with these "transparent persistence" layers that it tends to cost more work than what they save.
T. Norman
Monday, December 29, 2003
I wish it was a "transparent" persistence layer (or perhps "invisible" :)). Calling it a "persistence layer" (let alone a "layer") seems a bit too kind.
Walter Rumsby
Monday, December 29, 2003
OK, so you have some Class object, and you want to instantiate an object of that class.
The JVM does handle the Long -> long translation for you when you use reflection to *invoke* a constructor or method; it just doesn't do it when you try to *find* the constructor.
It sounds like you've got something like this:
Class c = ...; // the class you want to instantiate
Object[] arguments = ... ; // the arguments
// find the type of each argument
Class[] argtypes = new Class[arguments.length];
for (int i = 0; i < arguments.length; i++)
argtypes[i] = arguments[i].getClass();
// find the constructor and instantiate the object
Constructor constructor = c.getConstructor(argtypes);
return constructor.newInstance(arguments);
What you could do is get all the constructors, then search for one that matches your arguments:
// assuming c, arguments, argtypes same as above
Constructor[] con = c.getConstructors();
for (int i = 0; i < constructors.length; i++)
{
int j;
Class[] ct = con[i].getParameterTypes();
for (j = 0; j < ct.length; j++)
{
if (ct[j].isPrimitive())
{
if (
(ct[j].equals(Long.TYPE) && !argtypes[j].equals(Long.class)) ||
(ct[j].equals(Integer.TYPE) && !argtypes[j].equals(Integer.class)) ||
// etc, for all the primitive type / wrapper class combinations
)
break;
} else {
if (!ct[j].equals(argtypes[j]))
break; // constructor doesn't match arguments
}
}
if (j == ct.length)
{
return con[i].newInstance(arguments);
}
}
// throw an exception or whatever
This is similar to Scot Doyle's suggestion, except that you're iterating through the (probably short, and easy to acquire) array of Constructor objects, instead of searching your (probably longer) argument list for the primitive-wrapping types and trying each (wrapper, primitive) permutation in turn.
Daryl Oidy
Tuesday, December 30, 2003
Daryl's code is really good, not much to add here, except maybe a line
if (ct.length != arguments.length) continue;
right after
Class[] ct = con[i].getParameterTypes();
Otherwise, it will break if you happen to have a public default constructor (j == ct.length, j==0, but arguments.length != ct.length).
On the original topic: a major difference between Long.TYPE vs Long.class is that a Long value can be a <code>null</code>. This prevents Java from implementing a bullet-proof automatic conversion ever. Watch for nulls.
Walter: as you may know, many class libraries frameworks (different languages & platforms) impement persistence like this:
- base class declares persistence support;
- to create an object of a concrete class, always use a default constructor (usually protected). Should be enough in most cases, otherwise use factories of relevant complexity, as T. Norman mentioned;
- after constructing, the newly-created objects reads remaining properties itself (protected write()/read() methods are implemented in concrete classes).
For one thing, this approach does not break incapsulation, (members stay private, constuctors protected, except a factory object).
On the other hand, even if the reflection API is appropriate here, it creates a serious maintenance risk:
- other people do not understand how reflection works;
- or, much worse, they do or try to understand, decide it's cool, and use it everywhere. The result is a nightmare, you will consider Smalltalk a strongly-typed language after that.
Dmitri Chatokhine
Tuesday, December 30, 2003
Maybe I'm thick - I just still don't understand why reflection is required in this case. It seems to me that you just need to construct an object, based on what parameters you happen to be given at some point.
Reflection is only needed when you're dealing with unknown classes/methods (for example when serializing arbitrary classes), or as a last resort to gain access to protected fields or methods.
Some of the code below is left as an exercise for the reader, but it illustrates the basic idea:
Object[] args = ...;
if ( isSimpleKey(args) )
{
long arg1 = ((Long) args[0]).longValue();
long arg2 = ((Long) args[1]).longValue();
doSomething( new SimplePK(arg1, arg2) );
}
else if ( isComplexKey(args) )
{
doSomething( new ComplexPK(args) );
// or whatever args the constructor needs
}
else
{
// log error or whatever
}
On an additional note, the earlier code posted that uses reflection may not work because it assumes the arguments are in the array in the same order as the delcaration order or the parameters required by the constructor.
burninator
Tuesday, December 30, 2003
Can you extend that class with your own constructor with known parameters and not use reflection or always assume primitive types, or implement an interface which will do and simplify the work?
BTW, it is better when AClass(Long Long) calls BClass(long, long) (not vise versa) to save some memory taken by Long class members, 8 bytes per each class reference, it's matter if you have a lot of instances...
Evgeny Gesin /Javadesk.com/
Tuesday, December 30, 2003
Daryl, I like your example. Much cleaner than the trial and error method.
Scot Doyle
Tuesday, December 30, 2003
Re: Justification for use of reflection.
The situation is I have a text file that describes tables, table schema and data for those tables.
There is a 1:1 mapping between tables and persistence manager objects (already existing design). The objects I need to call are based on the tables listed in the file, so if I have:
table.X. ...
...
table.Y. ...
...
table.Z. ...
...
I need to talk to com.etc.persistence.XManager, com.etc.persistence.YManager, etc.
The import I'm writing needs to support this.
I'd be interested in alternative approaches not using reflection (well, at least intellectually, as I type this the code I've worked on is being released for testing :)).
Walter Rumsby
Tuesday, December 30, 2003
I'd suggest Hibernate (www.hibernate.org) it's a full featured persistance layer that is as lightweight and transparent as I've seen and it's relatively easy to use. After trying OJB first, Hibernate was much easier to install and get going with. At least you could check out the source code and maybe get some ideas on how they do things in their code (it also uses reflection).
Justin Kolb
Wednesday, December 31, 2003
Just a short note on Scot Doyle's suggestion for a constructor. Since you already have the constructor written for the (long, long) case, I would favor the following code as it avoids 2 instances of object construction and leverages your existing code:
public class A {
public A(long p1, long p2) {
// some code (already written)
}
public A(Long p1, Long p2)
throws NullPointerException {
this(p1.longValue(), p2.longValue());
}
}
Barett McGavock
Wednesday, December 31, 2003
Recent Topics
Fog Creek Home
|