Java Forum / General / February 2007
Weird behavior on allocation...what's going on?
Zerex71 - 13 Feb 2007 16:33 GMT Greetings,
I'm working on an application which will be very math-intensive, and requires the use of mathematical constructs known as quaternions. They are basically 4-vectors and that's all you need to know for this discussion.
Here is the offending code snippet in the main():
Quaternion q1 = new Quaternion(5.0, 7.0, 6.6, -8.0); q1.print(); Quaternion q2 = new Quaternion(-4.0, -3.0, 2.0, 1.0); System.out.println("q1 and q2"); q1.print(); q2.print(); Quaternion q3 = new Quaternion(); System.out.println("q3 before multiplication"); q3.print(); q3 = Quaternion.mul(q1, q2); System.out.println("q3 after multiplication"); q3.print();
Here is the output I get:
quaternion: 5.000 + 7.000i + 6.600j - 8.000k q1 and q2 quaternion: -4.000 - 3.000i + 2.000j + 1.000k quaternion: -4.000 - 3.000i + 2.000j + 1.000k q3 before multiplication quaternion: 0.000 + 0.000i + 0.000j + 0.000k q3 after multiplication quaternion: 0.000 + 0.000i + 0.000j + 0.000k
Can anyone explain to me what I'm missing? I'm assuming that by doing new, I get two separate instances, init'd as shown, so that q1 and q2 are not the same thing. FYI, most of the code in the Quaternion class is static, so I'm guessing it has to do with some subtle but insidious misuse of the static keyword in my class definition. Please help! Thanks.
Mike
Uwe Plonus - 13 Feb 2007 16:41 GMT > Greetings, > [quoted text clipped - 4 lines] > misuse of the static keyword in my class definition. Please help! > Thanks. Hi,
it seems that the static causes your problems. Without the class it is very difficult to show the problems but it seems that the real and imaginary parts are saved in statics and so only one instance of a quarternion can exists (what dows not make sense a lot).
Uwe
Zerex71 - 13 Feb 2007 16:48 GMT > > Greetings, > [quoted text clipped - 13 lines] > > Uwe Hi Uwe,
Thanks for your quick reply. Yes, I have the four coefficients stored as statics, because I need them for other operations which I want to be static, so the user can use the class for math operations without having to have a specific instance (although this is not a hard requirement). I just want them to be able to use code like the following:
q3 = Quaternion.mul(q1, q2)
rather than
q3.mul(q1, q2)
which kind of doesn't make sense because the mul() operation returns a Quaternion rather than assigning q3's member variables. But, if after some discussion I come to the realization that this is a "bad" way to do things, I will change the implementation.
Thanks for your reply.
Mike
Patricia Shanahan - 13 Feb 2007 16:58 GMT ...
> Thanks for your quick reply. Yes, I have the four coefficients stored > as statics, because I need them for other operations which I want to [quoted text clipped - 4 lines] > > q3 = Quaternion.mul(q1, q2) You can't just stick "static" on a variable without the consequence of all objects of the class seeing the same value, which does not make sense for the coefficients.
Your mul implementation should look like:
public static Quaternion mul(Quaternion q1, Quaternion q2){ Quaternion result = new Quaternion(); result.t = q1.t*q2.t - q1.x*q2.x - q1.y*q1.y - q1.z*q2.z; .... return result; }
The code replacing "..." must never reference a coefficient without qualifying it with q1, q2, or result.
Patricia
Chris Uppal - 13 Feb 2007 18:03 GMT > q3 = Quaternion.mul(q1, q2) I think most Java programmers would expect to write that as:
q3 = q1.mul(q2);
I.e. the mul() operation is an instance method of the Quaternion it is applied to. If you want the static methods as well, then you can define
public static Quaternion add(Quaternion q1, Quaternion q2) { return q1.add(q2); }
so that the quoted code snippet would also work.
BTW, you said you were considering making your Quaternions immutable. I think that's a good idea -- I just wanted to congratulate you on /not/ following the Sun Quaternion class. /They/ have chosen to make the things mutable, and in fact an expression like:
q1.add(q2);
/changes/ q1 in place to the the sum of the original value of q1, and the supplied value for q2 (which /isn't/ changed). I think that's a terrible bit of design...
(There might have been performance reasons for providing mutating operators which do change the Quaternion they are applied to, but if so then I don't think that add() is at all a good name -- addIn() might be better.)
-- chris
Zerex71 - 13 Feb 2007 19:27 GMT On Feb 13, 1:03 pm, "Chris Uppal" <chris.up...@metagnostic.REMOVE- THIS.org> wrote:
> > q3 = Quaternion.mul(q1, q2) > [quoted text clipped - 29 lines] > > -- chris Hi Chris,
Thanks for looking over the code. As far as your suggestion, writing it this way:
q3 = q1.add(q2)
is exactly something I don't want to do. To me, it just looks balky and I'm not a fan of doing it that way. But that's just me; I prefer things to look more like an expression, i.e. C = f(A,B) like C = Math.hypot(A,B). That kind of thing. I'm just now getting back into really learning how to get the most out of the Java language so there is a lot I want to investigate.
Unfortunately a lot of what happens to me when I write code, and I suspect this happens to others, is that I lay out how I think it should look in the code but then when I go to call it with real parameters and variables, I realize it either looks weird, won't compile, or doesn't seem "clean" or elegant enough. Of course, I could be being too picky -- after all, if I have a function thats purpose is to add two numbers and return their result, isn't that good enough, regardless of how it looks?
Also, as to the question of doing it myself...I realize one of the cool things about languages, Java especially, if the portability of packages and classes, and while I would trust "official" code from Sun et. al. I am trying to achieve two goals by writing such a class:
1. Apply what my O'Reilly books and other sources tell me about the nuances and features of Java to practical, useable examples 2. Develop real classes that will have function as part of my own application
I get neither experience from my current software engineering gig and am none too happy about that, so I code when I can.
Thank you all for writing. This is one of the better Abusenet discussions I've had lately. :)
Mike
Oliver Wong - 13 Feb 2007 20:55 GMT > On Feb 13, 1:03 pm, "Chris Uppal" <chris.up...@metagnostic.REMOVE- > THIS.org> wrote: [quoted text clipped - 51 lines] > more like an expression, > i.e. C = f(A,B) like C = Math.hypot(A,B). That kind of thing. I think one reason Chris suggested the q3 = q1.add(q2) syntax is that it's pretty idiomatic of Java. Strings and BigInteger, for example, both follow that pattern. So a Java programmer whose is used to using Strings and BigInteger can quickly guess what the syntax is for your Quaternion classes.
That said, notice that Chris suggested a way so that both formulations are available (i.e. q3 = q1.add(q2) would be equivalent to q3 = Quaternion.add(q1, q2)) with minimal hassle.
- Oliver
Alex Hunsley - 14 Feb 2007 10:22 GMT >> On Feb 13, 1:03 pm, "Chris Uppal" <chris.up...@metagnostic.REMOVE- >> THIS.org> wrote: [quoted text clipped - 54 lines] > follow that pattern. So a Java programmer whose is used to using Strings and > BigInteger can quickly guess what the syntax is for your Quaternion classes. I've always found that idiom a little annoying in a purist sense, although you're right in that it is a standard. I think that on first look, when you're not used to this standard, you might expect an instance method like q1.add(q2) to actually have an effect on the value of q1 - but it doesn't (it just returns the result). And I know that many people do trip up over this assumption, initially. To me, Quaternion.add(q1, q2) is clearer - a result will be returned.
> That said, notice that Chris suggested a way so that both formulations > are available (i.e. q3 = q1.add(q2) would be equivalent to q3 = > Quaternion.add(q1, q2)) with minimal hassle. Yup, if space isn't a big limit, I prefer implementing both forms when writing such a class, and giving the user the choice. lex
Chris Uppal - 15 Feb 2007 18:08 GMT > I've always found that idiom a little annoying in a purist sense, > although you're right in that it is a standard. I think that on first [quoted text clipped - 3 lines] > many people do trip up over this assumption, initially. > To me, Quaternion.add(q1, q2) is clearer - a result will be returned. That, or something like it, is why I quietly changed the name of the instance method from add() to plus() in my later example.
-- chris
Chris Uppal - 14 Feb 2007 19:02 GMT > Thanks for looking over the code. As far as your suggestion, writing > it this way: [quoted text clipped - 3 lines] > is exactly something I don't want to do. To me, it just looks balky > and I'm not a fan of doing it that way. I agree that it looks horrible for mathematical expressions. But then, I think
q3 = Quaternion.add(q1, q2)
looks horrible too (albeit not as bad). The only really acceptable syntax is
q3 = q1 + q2.
But that's not available in Java.
But there are a couple of advantages to using the standard method call syntax (besides the fact that it /is/ standard). One is the rather trivial point that it chains better than the external form.
q = q1.plus(q2).minus(q3);
rather than
q3 = Quaternion.add(Quaternion.add(q1, q2), q3);
The more important reason is that it is different semantically -- more flexible, since it's a message send rather than a mere function call.
For instance, Alex Hunsley's suggestion in this thread, of making Quaternion be an implementation of a general Field interface (or abstract base class) is only possible if addition is left to the Field instances themselves (they decide how they respond to plus()), rather than hardwiring the call by using a static method.
> Of course, I could be being too picky -- after > all, if I have a function thats > purpose is to add two numbers and return their result, isn't that good > enough, regardless of how > it looks? I don't think so (that you're being too picky). For one thing, if you have instincts which are telling you that you're not doing things "right", then the thing to do is listen to them (and train them to work ever better), not to ignore 'em. For a second thing, the "look" of the code is anything but a triviality. Consider the following for a simple demonstration:
q = Quaternion.substract(q1, q2);
If that adds the two Quaternions together then you certainly have a "function that [...] adds two numbers", but it is in no sense "good enough" -- in fact it is completely evil !
> I get neither experience from my current software engineering gig and > am none too happy about that, > so I code when I can. Good luck. Have fun !
-- chris
Alex Hunsley - 15 Feb 2007 01:18 GMT > But there are a couple of advantages to using the standard method call syntax > (besides the fact that it /is/ standard). One is the rather trivial point that [quoted text clipped - 14 lines] > they respond to plus()), rather than hardwiring the call by using a static > method. Surely the Field instances themselves could still handle the operations statically though...
Chris Uppal - 15 Feb 2007 18:09 GMT > > For instance, Alex Hunsley's suggestion in this thread, of making > > Quaternion be an implementation of a general Field interface (or [quoted text clipped - 4 lines] > Surely the Field instances themselves could still handle the operations > statically though... I'm sorry, but I don't understand what you mean by that. Could you give an example ?
-- chris
Alex Hunsley - 14 Feb 2007 10:21 GMT >> q3 = Quaternion.mul(q1, q2) > [quoted text clipped - 23 lines] > supplied value for q2 (which /isn't/ changed). I think that's a terrible bit > of design... Aargh! I didn't know the sun version did that. Naughty sun. I think it's much easier to go the immutable way, rather than mutable, when you then end up writing defensive methods in order to be sure your code is robust, even when used by third parties....
> (There might have been performance reasons for providing mutating operators > which do change the Quaternion they are applied to, but if so then I don't > think that add() is at all a good name -- addIn() might be better.) The minefield of method names and implied information!
Oliver Wong - 13 Feb 2007 16:48 GMT > Greetings, > [quoted text clipped - 35 lines] > misuse of the static keyword in my class definition. Please help! > Thanks. We would probably want to see the source code for Quaternion.mul(Quaternion,Quaternion).
Additional info which may help:
* Did you write the Quaternion class, or did a 3rd party write it? * If you know, are instances of the Quaternion immutable?
- Oliver
Oliver Wong - 13 Feb 2007 16:50 GMT >> Greetings, >> >> I'm working on an application which will be very math-intensive, and >> requires the use of mathematical constructs known as quaternions. >> They are basically 4-vectors and that's all you need to know for this >> discussion. [...]
> * Did you write the Quaternion class, or did a 3rd party write it? In either case, have you considered using Sun's Quaternion implementation?
http://java.sun.com/products/java-media/3D/forDevelopers/J3D_1_3_API/j3dapi/java x/vecmath/Quat4f.html http://java.sun.com/products/java-media/3D/forDevelopers/J3D_1_3_API/j3dapi/java x/vecmath/Quat4d.html
- Oliver
Zerex71 - 13 Feb 2007 17:13 GMT > >> Greetings, > [quoted text clipped - 13 lines] > > - Oliver No, because I want and need to do this myself to understand everything about them. I would trust Sun, but generally speaking I do not trust other people's code - even if it supposedly works, I still find that oftentimes they are sloppy in their style and not as elegant as it can be. Besides, I've trust Microsoft for years now and am getting tired of that relationship. :)
Mike
Oliver Wong - 13 Feb 2007 17:32 GMT >> >> Greetings, >> [quoted text clipped - 20 lines] > trust Microsoft for years now and am getting tired of that > relationship. :) No offense, but if their implementation of Quaternion works, and yours doesn't, then maybe the level of elegance of their code is higher than the bar you've set.
If you wish to learn Java programming, then it makes sense to write your own versions of stuff, for the practice of it. But you'll have to one day eventually learn to deal with other people's code, because software nowadays is too complex for a person to implement alone. And then there's also the principle of "don't reinvent the wheel".
- Oliver
Zerex71 - 14 Feb 2007 15:18 GMT > >> "Oliver Wong" <o...@castortech.com> wrote in message > [quoted text clipped - 36 lines] > > - Oliver Oliver,
I should have told you that I've been programming Java off and on for several years and have been a software engineer for about 16 years now. I think I know how to deal with other people's code, and most of the time, it's pretty lousy, so I'm fine with the bar I've set, thank you very much. :)
Mike
Alex Hunsley - 13 Feb 2007 18:21 GMT >>>> Greetings, >>>> I'm working on an application which will be very math-intensive, and [quoted text clipped - 13 lines] > No, because I want and need to do this myself to understand everything > about them. It's a good practice to write a Quaternion class yourself then. Two things:
1) to get the most out of this, get poeple (e.g. in this group) to review your code once you've finished - you may get useful feedback, like has already happend
2) it's worth testing your code actually works once you've finished. For example, writing unit tests to see if things behave 'normally' when you do quaternion arithmetic. This is where knowing properties of complex/quaternion arithmetic come in very handy. For example, if you know de Moivre's rule and all that, you can write some tests to check your quaternion multiplication is behaving sensibly in certain circumstances, and so on.
> I would trust Sun, but generally speaking I do not trust other > people's code - even if it supposedly works, > I still find that oftentimes they are sloppy in their style and not as > elegant as it can be. A lot depends on how trustworthy the publisher is, and widely used the code is. For example, I know that Jakarta Common's HTTPClient is used widely and that means something.
lex
Zerex71 - 13 Feb 2007 16:51 GMT > > Greetings, > [quoted text clipped - 45 lines] > > - Oliver Oliver,
Here's the class in its entirety...still under development:
package angle;
/** * @author mfeher * */ public class Quaternion { // Methods // Constructors Quaternion() { q0 = 0.0; q1 = 0.0; q2 = 0.0; q3 = 0.0; }
Quaternion(double q, double qi, double qj, double qk) { q0 = q; q1 = qi; q2 = qj; q3 = qk; }
// Conversion methods // Method to convert a quaternion to Euler angles static EulerAngles quat2euler(Quaternion q) { // First we need to convert to a DCM representation Matrix3x3D DCM = new Matrix3x3D(); DCM = Quaternion.quat2dcm(q); // Now, using the DCM, extract the Euler angles in radians double rollRad = Math.atan(DCM.item(1, 2) / DCM.item(2, 2)); double pitchRad = Math.asin(-1*DCM.item(0,2)); double yawRad = Math.atan(DCM.item(0, 1) / DCM.item(0, 0));
// Convert the Euler angles in radians to degrees and store them Angle roll = new Angle(Angle.rad2deg(rollRad)); Angle pitch = new Angle(Angle.rad2deg(pitchRad)); Angle yaw = new Angle(Angle.rad2deg(yawRad)); // Set the resultant Euler angles EulerAngles euler = new EulerAngles(roll, pitch, yaw); return euler; }
// Defined mathematical operations // All of these operations are static so they can be used without having // *this* quaternion possess real values static Quaternion add(Quaternion qa, Quaternion qb) { // This method performs the defined operation of addition on two quaternions // i.e. qc = qa + qb Quaternion qc = new Quaternion(); qc.q0 = (qa.q0 + qb.q0); qc.q1 = (qa.q1 + qb.q1); qc.q2 = (qa.q2 + qb.q2); qc.q3 = (qa.q3 + qb.q3); return qc; }
static Quaternion neg(Quaternion q) { // This method performs the defined operation of negation of a quaternion // i.e. q_neg = neg(q) Quaternion q_neg = new Quaternion(); q_neg.q0 = -1*q.q0; q_neg.q1 = -1*q.q1; q_neg.q2 = -1*q.q2; q_neg.q3 = -1*q.q3; return q_neg; }
static Quaternion sub(Quaternion qa, Quaternion qb) { // This method performs the defined operation of subtraction on two quaternions // i.e. qc = (qa - qb) Quaternion qc = new Quaternion(); qc.q0 = (qa.q0 - qb.q0); qc.q1 = (qa.q1 - qb.q1); qc.q2 = (qa.q2 - qb.q2); qc.q3 = (qa.q3 - qb.q3); return qc; }
static Quaternion mul(Quaternion qa, Quaternion qb) { // This method performs the defined operation of multiplication on two quaternions // i.e. qc = (qa * qb) // Later on we may implement 3x3 matrix operations whereby we can // use those appropriately rather than doing it "directly" like we do // here (the less elegant but no less accurate approach). Quaternion qc = new Quaternion();
qc.q0 = (qa.q0*qb.q0 - qa.q1*qb.q1 - qa.q2*qb.q2 - qa.q3*qb.q3); qc.q1 = (qa.q0*qb.q1 + qa.q1*qb.q0 + qa.q2*qb.q3 - qa.q3*qb.q2); qc.q2 = (qa.q0*qb.q2 + qa.q2*qb.q0 + qa.q3*qb.q1 - qa.q1*qb.q3); qc.q3 = (qa.q0*qb.q3 + qa.q3*qb.q0 + qa.q1*qb.q2 - qa.q2*qb.q1);
// Note that we can use this method to multiply vector quaternions, which // simply means that the scalar parts of such vectors are zero (i.e. we // don't need a separate multiplication method to achieve this). return qc; }
static Quaternion inv(Quaternion q) { // This method returns the inverse of a given quaternion // i.e. q_inv = q^-1 Quaternion q_inv = new Quaternion(); return q_inv; }
static Quaternion div(Quaternion qa, Quaternion qb) { // This method performs the defined operation of division on two quaternions // i.e. q_div = qa/qb Quaternion q_div = new Quaternion();
// First, take the inverse of qb, the denominator Quaternion qb_inv = Quaternion.inv(qb);
// Now multiply it by the numerator q_div = Quaternion.mul(qa, qb_inv); return q_div; }
static Quaternion conj(Quaternion q) { // This method returns the conjugate of a given quaternion // i.e. q_conj = ~q Quaternion q_conj = new Quaternion(); return q_conj; }
static Quaternion unit(Quaternion q) { // This method returns the unit quaternion of a given quaternion // i.e. q_unit = |q| Quaternion q_unit = new Quaternion(); return q_unit; }
static double index(Quaternion p, Quaternion q) { // Method to calculate the index of quaternions. If the quaternions are // right quaternions, then the index of a right quaternion is the axis of // the quaternion scaled by the ratio of lengths. return 0.0; }
static double scalar(Quaternion q) { // Method to determine the scalar part of a quaternion // Part of the representation in rectangular (Cartesian) coordinate return q0; }
static Vector right(Quaternion q) { // Method to determine the right part of a quaternion // Part of the representation in rectangular (Cartesian) coordinate Vector right = new Vector(q1, q2, q3); return right; }
static void tensor(Quaternion q) { // Method to determine the tensor part of a quaternion // Part of the representation in polar coordinates }
static void versor(Quaternion q) { // Method to determine the versor part of a quaternion // Part of the representation in polar coordinates }
// Later on we may add the "scalar" and "vector" getters for a quaternion.
// TODO: Change to regular DCM when we implement that static Matrix3x3D quat2dcm(Quaternion q) { // Method to convert a given quaternion into a DCM Matrix3x3D DCM = new Matrix3x3D(); // Change to regular DCM when we implement that DCM.array[0][0] = ((q.q0 * q.q0) + (q.q1 * q.q1) - (q.q2 * q.q2) - (q.q3 * q.q3)); DCM.array[0][1] = 2 * ((q.q1 * q.q2) + (q.q0 * q.q3)); DCM.array[0][2] = 2 * ((q.q1 * q.q3) - (q.q0 * q.q2)); DCM.array[1][0] = 2 * ((q.q1 * q.q2) - (q.q0 * q.q3)); DCM.array[1][1] = ((q.q0 * q.q0) - (q.q1 * q.q1) + (q.q2 * q.q2) - (q.q3 * q.q3)); DCM.array[1][2] = 2 * ((q.q2 * q.q3) + (q.q0 * q.q1)); DCM.array[2][0] = 2 * ((q.q1 * q.q3) + (q.q0 * q.q2)); DCM.array[2][1] = 2 * ((q.q2 * q.q3) - (q.q0 * q.q1)); DCM.array[2][2] = ((q.q0 * q.q0) - (q.q1 * q.q1) - (q.q2 * q.q2) + (q.q3 * q.q3)); return DCM; }
// Lastly, we implement the all-important rotate method - but we will need // a 3D vector class to do this Vector rot(Quaternion q, Vector v_old) { // Rotate a vector using defined quaternion transformation Vector v_new = new Vector(); // Perform transformation operations // v_new = q * v_old * q_inv return v_new; }
// Getter methods double q0() { return q0; } double q1() { return q1; } double q2() { return q2; } double q3() { return q3; }
// Print/inspect methods // May want to implement toString here instead void print() { // Method to print out this quaternion. // We need some simple handling based on the sign of each component of // the quaternion. String s0, s1, s2, s3; s0 = String.format("%.3f", q0);
if (q1 < 0) { s1 = String.format("- %.3f", -1*q1); } else { s1 = String.format("+ %.3f", q1); }
if (q2 < 0) { s2 = String.format("- %.3f", -1*q2); } else { s2 = String.format("+ %.3f", q2); }
if (q3 < 0) { s3 = String.format("- %.3f", -1*q3); } else { s3 = String.format("+ %.3f", q3); }
String q = String.format("quaternion: %s %si %sj %sk", s0, s1, s2, s3); System.out.println(q); }
// Attributes private static double q0; private static double q1; private static double q2; private static double q3; }
Alex Hunsley - 13 Feb 2007 17:07 GMT > Oliver, > > Here's the class in its entirety...still under development: > > package angle; [snip]
A little feedback on your code:
* it's conventional to put your member variables right at the top of the class * consider making your Quaternion class immutable. In other words, once a Quat. is made, it cannot be changed. To do this, make your i, j, k etc member vars be "public final". Once you do this, you could get rid of getters. Immutable objects also remove the chance to mangle objects that other bits of code are depending on not changing.
lex
Zerex71 - 13 Feb 2007 17:29 GMT > > Oliver, > [quoted text clipped - 16 lines] > > lex Thanks to everyone who helped - your suggestions will be taken under consideration! I didn't even consider making them immutable. You're right though, because now that I think about it, I can declare an instance of a quaternion and like most doubles, you never really need to change a particular double (I can always allocate another one as needed). There's a lot of good suggestions here that I will use. I'm just glad I wasn't as far off as I originally expected -- I'm having the double "challenge" of implementing the mathematics behind this class as well as dealing with the nuances of the Java language. But it will be so worth it when I'm finished.
Mike
Alex Hunsley - 13 Feb 2007 18:16 GMT > Thanks to everyone who helped - your suggestions will be taken under > consideration! I didn't even consider making them immutable. You're [quoted text clipped - 6 lines] > class as well as dealing with the nuances of the Java language. But > it will be so worth it when I'm finished. Glad to help. I'm interested in seeing your final class when you're finished too.
Patricia Shanahan - 13 Feb 2007 16:49 GMT > Greetings, > [quoted text clipped - 37 lines] > > Mike The code being static may not be a problem, but from your results I'm guessing that the variables representing the components of the quaternion are also static, so that at any given time all Quaternion objects have the same components, the most recently created or calculated values. For example, the "new Quaternion()" makes the components zero, and the multiplication is (0,0,0,0)*(0,0,0,0).
If I'm right, you need to remove "static" from those variables. You may also need to change some methods to either be non-static or to avoid using "this", explicitly or implicitly.
Patricia
Alex Hunsley - 13 Feb 2007 17:02 GMT > Greetings, > > I'm working on an application which will be very math-intensive, and > requires the use of mathematical constructs known as quaternions. > They are basically 4-vectors and that's all you need to know for this > discussion. All praise Hamilton, for ijk=-1!
> Here is the offending code snippet in the main(): > [quoted text clipped - 27 lines] > is static, so I'm guessing it has to do with some subtle but insidious > misuse of the static keyword in my class definition. Please help! Yup. Sounds like you've defined member variables in your Quaternion class as static, which is not going to behave the way you want (as you noticed). The 'new' keyword does indeed make a new object, but 'static' can be used to cause different objects to all share one piece of data.
In other words, your code is probably like this:
public class Quaternion { private static float real; private static float i; private static float j; private static float k;
public Quaternion(float real, float i, float j, float k) { this.i = i; // or you could write Quaternion.i = i this.j = j; this.k = k; this.real = real; } }
which isn't correct. To fix, just remove the 'static' from your member vars:
private float real; private float i; private float j; private float k;
Static member vars can be used for holding data *that applies to your whole class* or *data which you know is common to every instance you will make*.
For example, you might have a static member variable recording how many Quaternion objects had been made so far (and you increment this value in the constructor), so your code would look like:
public class Quaternion { private float real; private float i; private float j; private float k;
private static int numQuaternions = 0;
public Quaternion(float real, float i, float j, float k) { this.i = i; // or even Quaternion.i = i this.j = j; this.k = k; this.real = real;
Quaternion.numQuaternions++; // we just made another }
// note that this method has to be static in order to // access the static membar variable, and hence you should // call it using Quaternion.getNumQuaternionsMade() public static int getNumQuaternionsMade() { return numQuaternions; } }
(Note that this isn't thread safe, but that's another topic.)
HTH, lex
Alex Hunsley - 14 Feb 2007 10:16 GMT > Greetings, > > I'm working on an application which will be very math-intensive, and > requires the use of mathematical constructs known as quaternions. > They are basically 4-vectors and that's all you need to know for this > discussion. [snip] Btw Zerex, here's a nice little idea... If you can find a Java mathematical expression parser (e.g. http://www.singularsys.com/jep/), but one that supports arithmetic on arbitrary systems that you provide classes for (e.g. Quaternion), you could also hook that up. The end result is that your class could provide functionality which would let the user evaluate an arbitrary algebraic string expression, e.g.:
Hashtable values = new Hashtable(); values.put("a", new Quaternion(1, 2, 3, 4.6)); values.put("b", new Quaternion(0, 0, -1, 3)); values.put("c", new Quaternion(-10.4, 0, 0, 4));
Quaternion result = Quaternion.evaluate("(a*b - c^2)/2");
Or, of course, you (or I) could write an arbitrary expression evaluator ourselves (or modify an existing one, if possible). The key to writing it would be that the end-user provides a class (e.g. Quaternion) that implements an interface, 'AlgebraicField', which means that add, sub, mult and divide methods are provided... (of course, this interface's method would be taking Objects and nothing more specific, in order to remain generalised). Then just turn the expression into a prefix stack for evaluation, evaluate, and voila!
lex
Lew - 14 Feb 2007 20:09 GMT > Hashtable values = new Hashtable(); > values.put("a", new Quaternion(1, 2, 3, 4.6)); > values.put("b", new Quaternion(0, 0, -1, 3)); > values.put("c", new Quaternion(-10.4, 0, 0, 4)); For my edification, why did you choose Hashtable over other Map implementations?
- Lew
Oliver Wong - 14 Feb 2007 21:27 GMT >> Hashtable values = new Hashtable(); >> values.put("a", new Quaternion(1, 2, 3, 4.6)); [quoted text clipped - 3 lines] > For my edification, why did you choose Hashtable over other Map > implementations? I'm guessing it's for the O(1) lookup, and 'cause order doesn't really need to be preserved. These are names of variables used in the expression that appears later: "(a*b - c^2)/2".
- Oliver
Daniel Pitts - 14 Feb 2007 22:32 GMT > >> Hashtable values = new Hashtable(); > >> values.put("a", new Quaternion(1, 2, 3, 4.6)); [quoted text clipped - 9 lines] > > - Oliver Whats that have to do with Map vs Hashtable? If the question was "Why aren't you using SortedMap", then your response makes sense. Ohwell.
Map<String, Quaternion> values = new HashMap<String, Quaternion>();
values.put("a", new Quaternion(1, 2, 3, 4.6)); values.put("b", new Quaternion(0, 0, -1, 3)); values.put("c", new Quaternion(-10.4, 0, 0, 4));
Oliver Wong - 15 Feb 2007 15:42 GMT >> >> Hashtable values = new Hashtable(); >> >> values.put("a", new Quaternion(1, 2, 3, 4.6)); [quoted text clipped - 17 lines] > values.put("b", new Quaternion(0, 0, -1, 3)); > values.put("c", new Quaternion(-10.4, 0, 0, 4)); I was focusing on the keywords "other implementations" in "other Map implementations". AFAIK, the other main Map implementation (TreeMap) doesn't provide O(1) lookup, and the rests are for "special" situations (e.g. EnumMap, WeakHashMap, ConcurrentHashMap, IdentityHashMap, etc.)
- Oliver
Daniel Pitts - 15 Feb 2007 18:03 GMT > >> "Lew" <l...@nospam.lewscanon.com> wrote in message > [quoted text clipped - 28 lines] > > - Oliver You do realize there is a different between the Hashtable and HashMap classes, right? I believe that is what Lew was asking about. Hashtable is retained for backward compatibility only.
Oliver Wong - 15 Feb 2007 18:53 GMT >> >> "Lew" <l...@nospam.lewscanon.com> wrote in message >> [quoted text clipped - 32 lines] > classes, right? I believe that is what Lew was asking about. > Hashtable is retained for backward compatibility only. Oops. I missed that part.
- Oliver
Chris Uppal - 15 Feb 2007 19:17 GMT > I was focusing on the keywords "other implementations" in "other Map > implementations". AFAIK, the other main Map implementation (TreeMap) > doesn't provide O(1) lookup, and the rests are for "special" situations > (e.g. EnumMap, WeakHashMap, ConcurrentHashMap, IdentityHashMap, etc.) I think the issue was: why use the ancient, and largely superseded, implementation of Map called Hashtable, instead of its (mostly) equivalent replacement, HashMap ?
(Same slight irritation as seeing Vector used where ArrayList would be better.)
-- chris
Alex Hunsley - 15 Feb 2007 22:42 GMT >> I was focusing on the keywords "other implementations" in "other Map >> implementations". AFAIK, the other main Map implementation (TreeMap) [quoted text clipped - 6 lines] > > (Same slight irritation as seeing Vector used where ArrayList would be better.) Actually, I was still in J2ME mode when I wrote that post - hence Hashtable, which is available, whereas Hashmap ain't. (Similarly, there's no ArrayList - you have to use Vector.)
> -- chris Lew - 16 Feb 2007 01:23 GMT > Actually, I was still in J2ME mode when I wrote that post - hence > Hashtable, which is available, whereas Hashmap ain't. (Similarly, > there's no ArrayList - you have to use Vector.) Aha!
- Lew
Zerex71 - 22 Feb 2007 19:36 GMT In my continued question to implement some code, I ran across another question.
I have a Vector class which is intended for use as a true, real vector (mathematical), not the misnamed kind of "collection" class that it usually it refers to. It is subclassed from the generic Matrix class which contains the actual data members (a 1,3 matrix storing three doubles in an array called, oddly enough, "array"). Whenever I perform an operation that modifies the values of Matrix.array, I want Vector to also have its attributes (double x,y,z) to also be updated.
Here's the catch: Much of what I'm doing with the Vector right now is static, so I'm only using the class to perform operations for me, not so much to store content (although I will be using it for that just as much very soon). My question is, if I invoke Vector.operation() which returns another Vector, how does the underlying array even get set? How do x,y, and z get updated if I am using it statically?
Here's an example:
v_new = Vector.scale(v_old, k); // Standard scaling of a vector by a length factor k ... a = v_new.x(); // These are getter methods named identical to their member variables b = v_new.y(); c = v_new.z();
Also,
class Matrix { double array[][]; ... }
class Vector extends Matrix { double x; // Any time the underlying array[][] elements get updated, these should be too double y; double z; ... }
This implementation may be overkill, but I like the elegance of taking this approach. Also, the x,y,z and their corresponding getters are really for convenience - "x" is a lot easier to type than "array[0][0]". That sort of thing.
I'm sure I'm not explaining this fully but I think you get the idea. Discuss!
Lew - 22 Feb 2007 19:45 GMT A new topic deserves a new thread.
> I have a Vector class which is intended for use as a true, real vector > (mathematical), not the misnamed kind of "collection" [quoted text clipped - 35 lines] > "x" is a lot easier > to type than "array[0][0]". That sort of thing. Don't have instance variables for x, y and z. Define the methods in terms of the underlying, inherited array.
public double x() { return array [0] [0]; }
- Lew
Zerex71 - 22 Feb 2007 21:02 GMT > A new topic deserves a new thread. > [quoted text clipped - 47 lines] > > - Lew Your last suggestion I can live with. I just wanted to use the x, y, and z names as ways to reference. That still leaves the question of how the array gets updated if I am using the class in a static fashion i.e. not setting its individual members to particular values. As to the former suggestion, it's a continuation of an earlier topic, so sorry.
Lew - 22 Feb 2007 22:28 GMT Lew wrote:
>> Don't have instance variables for x, y and z. Define the methods in terms of >> the underlying, inherited array. [quoted text clipped - 3 lines] >> return array [0] [0]; >> }
> Your last suggestion I can live with. I just wanted to use the x, y, > and z names as ways to reference. > That still leaves the question of how the array gets updated if I am > using the class in a static fashion > i.e. not setting its individual members to particular values. Use an appropriate setBlah( double val ) method.
Or perhaps setBlah( double val, int row, int col ).
This business of hiding all attributes behind accessor methods is quite powerful, at the cost of verbosity. A method pair "setX()" and "getX()" need never reference an actual instance variable "x" within the black box. They only need to present to API clients the view of "x" as an attribute.
Don't use static members unless you really intend the value to be what it is for the entire class. If your analytical model says, "I can have two Vectors, call them 'va' and 'vb', and they can contain separate values, and not be equal in value to each other", then the model requires that the "values" for Vectors be instance-level.
While you're at it, you will want to override the Object methods equals() and hashCode(), in a consistent manner.
You also face the decision of whether to make Matrix instances immutable, in effect "read-only" after construction. There are distinct benefits to doing so, but it complicates objects that support calculations.
Joshua Bloch covers these overrides and immutability admirably in his book /Effective Java/, and there are many articles on these topics. GIYF. So are IBM DeveloperWorks and Sun's Java sites.
P.S., in UMLish diagrams and class design documentation, I model accessor and mutator methods as attributes, not methods. At times some of my colleagues want to change the design docs to match the physical implementation as methods. This is, of course, working the wrong way around.
- Lew
Zerex71 - 23 Feb 2007 14:36 GMT > Lew wrote: > >> Don't have instance variables for x, y and z. Define the methods in terms of [quoted text clipped - 42 lines] > > - Lew Hi Lew,
The thing with using the static functionality is that it's an area of use that I want to investigate. Not only am I developing an application but I'm also expanding my thoughtview of the language. To that end, I am learning how best to take advantage of static vs. non- static. In other words, I often have the need for a class to do some utility on behalf of some other code, but not necessarily to have an instance, and yet, the class can bind up instance-based methods and attributes in one place (if any of this makes any sense).
In other words, I want Vector to represent individual instances of unchangeable values (I don't modify a vector but will instead, operate on it to produce another result/instance), but I also want Vector to have operations that can be used without requiring an instance (although in pure terms this is not *that* likely to happen -- I won't usually ask for the unit vector without having an actual vector to perform that operation on).
Thanks for writing.
Zerex71 - 23 Feb 2007 14:43 GMT > Lew wrote: > >> Don't have instance variables for x, y and z. Define the methods in terms of [quoted text clipped - 18 lines] > never reference an actual instance variable "x" within the black box. They > only need to present to API clients the view of "x" as an attribute. So I'll ask a silly question - if I have an attribute (double x), would it be permissible to have x(double) as the setter and x() as the getter?
> Don't use static members unless you really intend the value to be what it is > for the entire class. If your analytical model says, "I can have two Vectors, > call them 'va' and 'vb', and they can contain separate values, and not be > equal in value to each other", then the model requires that the "values" for > Vectors be instance-level. In my case, nothing will be constant about *all* instances of Vector. They will all potentially have varying x,y,z values. So, put that way, perhaps I need not have member variables in a static class....I don't want to do this, but maybe I need a Vector class (non-static) and a VectorOps class (static).
Patricia Shanahan - 23 Feb 2007 15:54 GMT ...
> So I'll ask a silly question - if I have an attribute (double x), > would it be permissible > to have x(double) as the setter and x() as the getter? Technically, it would work because the two methods have different signatures.
From a human factors point of view, I don't think it is really desirable to have two methods in the same class with the same name that do very different things. Usually, a method with the same name as another, but omitting a parameter, is a convenience method that does the same job with a default value for the parameter.
Patricia
Zerex71 - 23 Feb 2007 16:17 GMT > ... > [quoted text clipped - 12 lines] > > Patricia But it is permissible, right? That is method overloading, if I recall correctly. I think with a trivial member variable like x, having getX() and setX(double) seem to be overkill, although I am not opposed to it.
Mike
Patricia Shanahan - 23 Feb 2007 16:35 GMT >> ... >> [quoted text clipped - 17 lines] > setX(double) seem to > be overkill, although I am not opposed to it. Depends what you mean by "permissible". I would not do it, because I set a higher value on the principle of minimum surprise than on terseness when designing class interfaces.
Also, thinking in terms of "a trivial member variable" is a mistake.
There is some attribute that objects of your class all have. You have decided it is appropriate for users of the class to be able to get and set it. That is a matter of interface design, so changing it can affect any code that uses the class.
You have also decided, at least for now, that the attribute is best implemented by keeping its current value in a member variable. That is just a matter of implementation of this class, and could be changed without any change in the interface.
Patricia
Chris Uppal - 23 Feb 2007 16:45 GMT > So I'll ask a silly question - if I have an attribute (double x), > would it be permissible > to have x(double) as the setter and x() as the getter? Yes, it's perfectly permissible, and even rather sensible (in this case).
I should warn you that it does break a fairly widely-used convention for Java, where methods tend to come in getX()/setX(int) pairs. But it is only a convention, and not (IMO) a particularly good one.
-- chris
Zerex71 - 23 Feb 2007 20:30 GMT On Feb 23, 11:45 am, "Chris Uppal" <chris.up...@metagnostic.REMOVE- THIS.org> wrote:
> > So I'll ask a silly question - if I have an attribute (double x), > > would it be permissible [quoted text clipped - 7 lines] > > -- chris Hi Chris,
I think part of the reason I bring this up is that when all I want to do is set a particular value for my class (Vector, for example, but this could apply to other classes), all I really want to do is:
MyVector.x = ... MyVector.y = ... MyVector.z = ...
But we're supposed to be all "OO" and everything so I'm supposed to hide my data, right? So I have to use the overhead of MyVector.setX(...) etc. which I think is just overkill. Plus, I agree with the tenets of "gOOd design" but in all reality, it's not like thousands of people are going to be using my class and strewing the Java landscape with my class. It's one class in one application. If I can design it to be airtight for my purposes, I think that's just fine.
One big thing that I have discovered in my coding is that most of the time it almost doesn't matter how I implement the class, the ultimate thing that drives my design is the elegance, look, and soundness of the code *when it is actually invoked*. In other words, if I do the bottom-up thing, all's well and good until the first time I try to use what I've coded and then I go, "Uh-oh, that doesn't look too great" so I go back and restructure things.
Mike
Martin Gregorie - 24 Feb 2007 15:25 GMT > One big thing that I have discovered in my coding is that most of the > time it almost doesn't matter how I implement the class, the ultimate [quoted text clipped - 3 lines] > what I've coded and then I go, "Uh-oh, that doesn't look too great" so > I go back and restructure things. This is a good reason for not using bottom up design. IMO top-down design combined with incremental development is the way to go. This way you can introduce a new class with sensible (and sensibly named) methods, test run it as a skeleton that does little more than return values needed to check the operation of its caller and only then, when the logic is right, do you need to write functionality into the new class.
I really hate seeing undocumented classes (there are some in the standard class library and more in Javamail and 3rd party packages). As skeletal descriptions are better than none I think the outline Javadoc descriptions are best written as part of the class skeleton and then fleshed out as code is added to the methods.
 Signature martin@ | Martin Gregorie gregorie. | Essex, UK org |
angrybaldguy@gmail.com - 25 Feb 2007 07:29 GMT > On Feb 23, 11:45 am, "Chris Uppal" <chris.up...@metagnostic.REMOVE- > [quoted text clipped - 4 lines] > > > Yes, it's perfectly permissible, and even rather sensible (in this case). Just to clarify Chris's response a bit:
You can safely give accessors and mutators (or any other methods) the same name provided they have different signatures (name + arguments, basically). That said, the getFoo/setFoo convention is VERY strongly embedded in the Java programming culture. It's important to obey local conventions for the simple reason that if you ever need help with your code, you'll spend less time explaining what getX() is doing than what x() is doing. That said...
> I think part of the reason I bring this up is that when all I want to > do is set a particular value for my class (Vector, for example, but [quoted text clipped - 6 lines] > But we're supposed to be all "OO" and everything so I'm supposed to > hide my data, right? Yes, but accessors and mutators have very little to do with data hiding.
- WARNING - WARNING - WARNING - The following viewpoint is extremist and not completely practical on all projects. Take with a grain of salt as large as needed. - WARNING - WARNING - WARNING -
"OO" doesn't mean "I have accessors and mutators for all my member variables". It means the code is structured around the behaviour of each object as a whole rather than treating objects as glorified structs. Think about *why* you want to change just one attribute of your mathematical Vector. In fact, think carefully about why you want to change the Vector at all.
I'm strongly opposed to mutable "value" objects; they complicate making assertions about the thing the object represents signifigantly. Imagine for a moment if String were mutable -- every time you passed a String into or received a String from a method outside your control, the contents of the String might change without the identity of the String changing. Conversely, you'd be able to violate "invariants" of other classes by giving them a String in a valid state and then changing it to be an invalid state:
HashMap<String, T> stringMap = ....; String keyA = "key a"; String keyB = "key b";
stringMap.put (keyA, valueA); stringMap.put (keyB, valueB);
keyB.setChar(4, 'a');
And there's nothing HashMap could do to "defend" itself from this without adding a large amount of Observer-pattern cruft to String and complicating the class, or without copying the key Strings and using copies instead of the original string, consuming much more memory.
To get back to Vectors and operations on them, consider working with entire vectors at once rather than vector components. Component operations should be encapsulated inside the Vector objects (as instance methods) or inside the Vector class (as static methods). The "x", "y", and "z" components may be exposed through accessors safely, but not mutated outside the class.
Then if you need to represent a new mathematical vector, you represent it with a new Vector instance either created with "new" or created by operations on existing Vector instances, similar to how String's "manipulation" methods like replace return new Strings. If you no longer need the original Vector, dispose of it and let the GC deal with it. If you're reasonably smart about how you use Vector objects, you can avoid creating excess objects by keeping computed Vectors around until you're sure you don't need them any more, which should alleviate most "heap churn" concerns.
Chris Uppal - 25 Feb 2007 19:20 GMT > One big thing that I have discovered in my coding is that most of the > time it almost doesn't matter how I implement the class, the ultimate > thing that drives my design is the elegance, look, and soundness of > the code *when it is actually invoked*. I know what you mean, and I too have had the sad experience of designing something which made perfect sense on its own terms but where turned out that I hadn't given enough thought given to its "user-interface".
But I think you may be overdoing it a little. The superficial appearance of the code is comparatively unimportant (within limits) -- less important than semantic coherence anyway. If you have to sacrifice one to get the other (and you /will/ have to, if you are programming in Java) then prefer clean, manageable, and complete semantics every time.
If (once you've got the semantics right) you can tweak a few things, or introduce a few helpers and short-cut methods, to make the client code prettier, then that's fine. No reason not to go for it. But don't make the mistake on not providing a coherent, complete, "ordinary", API for places where you don't want to prettify the user code.
Sauce on top of pasta, not sauce /instead/ of pasta...
-- chris
John W. Kennedy - 23 Feb 2007 23:21 GMT >> So I'll ask a silly question - if I have an attribute (double x), >> would it be permissible [quoted text clipped - 5 lines] > where methods tend to come in getX()/setX(int) pairs. But it is only a > convention, and not (IMO) a particularly good one. It's even a little bit more than a convention, in that Eclipse (I know) and NetBeans (I suppose) will automatically generate them for you, and in that they are mandatory in the JavaBeans architecture.
 Signature John W. Kennedy "The blind rulers of Logres Nourished the land on a fallacy of rational virtue." -- Charles Williams. "Taliessin through Logres: Prelude"
Chris Uppal - 23 Feb 2007 16:51 GMT >.I don't want to do this, but maybe I need > a Vector class (non-static) and a VectorOps class (static). There is no need for that, since static members are separate from instance members anyway. But if it helps you keep the separation clear in your head while you are learning how to use "static" effectively, then it might be a good idea to try for a while.
-- chris
Alex Hunsley - 15 Feb 2007 01:11 GMT >> Hashtable values = new Hashtable(); >> values.put("a", new Quaternion(1, 2, 3, 4.6)); [quoted text clipped - 3 lines] > For my edification, why did you choose Hashtable over other Map > implementations? There wasn't a lot of thought put into the particular choice of which map I used in the example! There may be more suitable choices. I did start thinking about it when I wrote the post but realised I was dallying too much when I had work to do, so put that particular reading on hold and just sent my post. lex
> - Lew
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|