Orangy Tang
JGO Kernel      Posts: 2959 Medals: 37
Monkey for a head
|
 |
«
on:
2003-08-12 16:36:40 » |
|
Now I may be totally off the mark here, so maybe people can put me straight.. Imagine the handy little class 'Vector2f', just a simple little math class. Now in C++ I'd happily write: 1
| const Vector2f getPosition(); |
to get an objects position, safe in the knowledge that who ever is calling the method can't tinker with the returned object. All well and good. But the Java equivilent would be without any sort of 'const' modifier, and so open up the class to be modified in an unintended way, bypassing the intended set method. 'final' to the best of my knowledge, just makes the reference final, not the actual data, and is therefore not a substiture. So the alternative would be 1
| void getPosition(Vector2f dest); |
and having a blank object into which the new information is copied. That or return a copy, but both of these need an extra object to be created just to hold what will likely be a temporary result. Now I may be being overly paranoid about the GC, but this is getting to be really annoying. I don't like possibly creating hundreds of tiny little objects per frame, and the alternative of keeping them around (or a small number of temporary ones around and reusing them) gets ugly when used to any large extent.
|
|
|
|
Athomas Goldberg
Sr. Member   Posts: 284
Grrrrrr...
|
 |
«
Reply #1 on:
2003-08-12 17:41:25 » |
|
If the Vector2f class is yours you may want to consider creating an immutable version of it. Something like: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public interface Vector2f { public float getX(); public float getY(); }
public class ImmutableVector2f implements Vector2f {
private final float x; private final float y; public ImmutableVector2f(float x, float y) { this.x = x; this.y = y; }
public float getX() { return x; }
public float getY() { return y; } }
public class MutableVector2f implements Vector2f { private float x; private float y; public MutableVector2f(float x, float y) { this.x = x; this.y = y; }
public float getX() { return x; }
public float getY() { return y; } public void setX(float x) { this.x = x; }
public void setY(float y) { this.y = y; } } |
It's not quite the same as const, but it gets you at least part of the way there.
|
Athomas Goldberg Project Lead / Wildcard Game Technologies Group Sun Microsystems, Inc.
|
|
|
Athomas Goldberg
Sr. Member   Posts: 284
Grrrrrr...
|
 |
«
Reply #2 on:
2003-08-12 17:43:47 » |
|
Oops. disregard my last missive. I misread your post 
|
Athomas Goldberg Project Lead / Wildcard Game Technologies Group Sun Microsystems, Inc.
|
|
|
Games published by our own members! Go get 'em!
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #3 on:
2003-08-12 19:06:35 » |
|
There are little tricks you can do. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class Vector2F { int getX() { ... } int getY() { .. } }
public class MutableVector2F extends Vector2F { void setX(int x) { ... } void setY(int y) { ... } }
public class MyClass { private MutableVector2F myVector; public Vector2F getPosition() { return myVector; } } |
You're returning your vector truncated as the immutable base class. Only you can (honestly) alter it. You could take it one step further and make MutableVector2F a private class of your class. That offers the same protection as C++ does, generally. It prevents honest use of your class from accidentally damaging things. Dishonest use is possible, sure, just cast it down to the mutable form. But that's deliberate, and if you're own code is doing that, then something else is wrong. If someone else is deliberately trying to mess up your vector, then C++ offers you the same shakey safety, casting away constness is very easy. Incidentally a lot of Swing classes take it to the extreme and return a copy of the object. Ugh.
|
|
|
|
|
Athomas Goldberg
Sr. Member   Posts: 284
Grrrrrr...
|
 |
«
Reply #4 on:
2003-08-12 21:17:44 » |
|
Since we're on the subject of tricks you could make your an inner class with private member variables, which would only be accessible to the outer class, like so: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Vector2F { public float getX() { ... } public float getY() { .. } }
public class MyClass {
private MyVector2f position = new MyVector2f();
public void setPosition(float x, float y) { position.x = x; poxition.y = y; }
public Vector2f getPosition() { return position; }
private static class MyVector2f extends Vector2f { private float x; private float y; public float getX() { return x; } public float getY() { return y; } } } |
Unlike the previous example, the Vector2f returned from getPosition() can't be cast into a mutable form, but thanks to scoping your outer class can modify it. This of course assumes that Vector2f isn't final or contains so many methods that overriding it this way is a pain in the @ss. It does however do what you want it to, namely prevent someone else from modify the object while giving your class the ability to, if in a somewhat convoluted way.
|
Athomas Goldberg Project Lead / Wildcard Game Technologies Group Sun Microsystems, Inc.
|
|
|
kevglass
« League of Dukes » JGO Kernel      Posts: 5214 Medals: 49
Mentally unstable, best avoided.
|
 |
«
Reply #5 on:
2003-08-13 00:28:11 » |
|
All this is besides the point that (C++ style) const would be damn useful. I've often wonder why Java didn't pick it up from C++ on the way...
Does anyone know why?
Kev
|
|
|
|
Orangy Tang
JGO Kernel      Posts: 2959 Medals: 37
Monkey for a head
|
 |
«
Reply #6 on:
2003-08-13 01:57:40 » |
|
This of course assumes that Vector2f isn't final or contains so many methods that overriding it this way is a pain in the @ss. It does however do what you want it to, namely prevent someone else from modify the object while giving your class the ability to, if in a somewhat convoluted way.
Thats quite a neat trick, I'll probably use that at some point, but in this actual case there are a lot of methods and to have to do this everywhere needed would be a large amount of work for not enough gain :-/ Would be nice to get a proper 'const', but I guess most people think that returning a copy is an acceptable solution :-[
|
|
|
|
princec
« League of Dukes » JGO Kernel      Posts: 8073 Medals: 91
Eh? Who? What? ... Me?
|
 |
«
Reply #7 on:
2003-08-13 04:50:11 » |
|
Nah, returning a copy is a sucky solution  Const is a handy keyword which is really a shortcut for creating tons of irritating get methods and read-only interfaces. I like programming shortcuts. Every byte I type is a byte that can go wrong :/ Cas 
|
|
|
|
cfmdobbie
JGO Wizard     Posts: 1257
Who, me?
|
 |
«
Reply #8 on:
2003-08-13 05:47:11 » |
|
Const was left out because it was deemed unnecessary, and a complication. Much like unsigned, enum, generics, multiple inheritance and all the other interesting stuff people still clamour to add! Vote here: http://developer.java.sun.com/developer/bugParade/bugs/4211070.html
|
Hellomynameis Charlie Dobbie.
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #9 on:
2003-08-13 06:29:13 » |
|
const is nice, but it does complicate things. I think Sun just wanted to keep Java slim and straightforward. Although I do think the omission of unsigned was a complete sin 
|
|
|
|
|
Games published by our own members! Go get 'em!
|
|
Athomas Goldberg
Sr. Member   Posts: 284
Grrrrrr...
|
 |
«
Reply #10 on:
2003-08-13 07:33:48 » |
|
Much like unsigned, enum, generics, multiple inheritance and all the other interesting stuff people still clamour to add! Well, at least enums and generics will finally be showing up in 1.5. And const has been set aside as a keyword in Java, so... who knows? That said, I wouldn't hold my breath waiting for multiple inheritence. 
|
Athomas Goldberg Project Lead / Wildcard Game Technologies Group Sun Microsystems, Inc.
|
|
|
cfmdobbie
JGO Wizard     Posts: 1257
Who, me?
|
 |
«
Reply #11 on:
2003-08-13 07:47:34 » |
|
No, agreed! Multiple inheritance causes more problems than it's worth (although I can come up with persuasive reasons to have it). Operator overloading is another thing I'm glad to see the back of, after a fashion! Unsigned really used to annoy me - I mean, what's an unsigned byte? That just doesn't make any sense! You get used to it after a while though. 
|
Hellomynameis Charlie Dobbie.
|
|
|
Athomas Goldberg
Sr. Member   Posts: 284
Grrrrrr...
|
 |
«
Reply #12 on:
2003-08-13 07:54:11 » |
|
Thats quite a neat trick, I'll probably use that at some point, but in this actual case there are a lot of methods and to have to do this everywhere needed would be a large amount of work for not enough gain :-/ Ooh, wait... Here's another flavor of the solutions: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| public class Vector2F { public float getX() { ... } public float getY() { .. } public void setX(float x) throws ImmutableException { ... } public void setY(float y) throws ImmutableException { ... }
} public class MyClass { private MyVector2f position = new MyVector2f(); public void setPosition(float x, float y) { position.secretSetX(x); poxition.secretSetY(y); } public Vector2f getPosition() { return position; } private static class MyVector2f extends Vector2f { public void setX(float x) throw new ImmutableException(); } public void setY(float y) throw new ImmutableException(); } private void secretSetX(float x) { try { super.setX(x); } catch(ImmutableException e) { } private void secretSetY(float y) { try { super.setY(y); } catch(ImmutableException e) { } } } |
Same result as previous solution, but all you have to do is reimplement your "set" methods. You can also make the MyVector2f and the "secret" methods package local, in which case you'd only have to implement MyVector2f once per package that was going to use it. I'm a stong believer in scoping classes to the scope of their use. If a class is only used within another class, I make it an inner class. If it's only used by other classes in a given package, I make it package local, etc, etc But that's another debate altogether.
|
Athomas Goldberg Project Lead / Wildcard Game Technologies Group Sun Microsystems, Inc.
|
|
|
Orangy Tang
JGO Kernel      Posts: 2959 Medals: 37
Monkey for a head
|
 |
«
Reply #13 on:
2003-08-13 07:56:06 » |
|
Well, at least enums and generics will finally be showing up in 1.5. And const has been set aside as a keyword in Java, so... who knows?
Yes, but then again... so it goto...  Operator overloading is another thing I'm glad to see the back of, after a fashion! 1
| String whatAbout = "String " + "concanenation?"; |
|
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #14 on:
2003-08-13 07:57:39 » |
|
except your setX() never actually sets anything, it just throws the exception. So your secretSetX() won't actually set anything either.
|
|
|
|
|
Athomas Goldberg
Sr. Member   Posts: 284
Grrrrrr...
|
 |
«
Reply #15 on:
2003-08-13 08:00:10 » |
|
Personally, I think the existance of the "Immutable" design pattern and the need for convoluted solutions like those detailed above, should be argument enough for adding const, but that's just me. I'm sure there are a host of reasons, technical and otherwise, for it's exclusion, but it certainly would be nice.
|
Athomas Goldberg Project Lead / Wildcard Game Technologies Group Sun Microsystems, Inc.
|
|
|
Athomas Goldberg
Sr. Member   Posts: 284
Grrrrrr...
|
 |
«
Reply #16 on:
2003-08-13 08:05:50 » |
|
except your setX() never actually sets anything, it just throws the exception. So your secretSetX() won't actually set anything either. secretSetX() (in MyVector2f) calls super.setX() (setX() in Vector2f) which presumably has some code to replace the "..." shorthand to actually set the value of X.
|
Athomas Goldberg Project Lead / Wildcard Game Technologies Group Sun Microsystems, Inc.
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #17 on:
2003-08-13 08:10:13 » |
|
One of the biggest reasons for having const is passing arguments by const reference. Without pass by reference in Java, const loses a lot of use. Even Orangy Tang's example is usually not done quite that way in C++, usually a const reference is returned. secretSetX() (in MyVector2f) calls super.setX() (setX() in Vector2f) which presumably has some code to replace the "..." shorthand to actually set the value of X.
Ah yup, just being stupid. Too early in the morning for me 
|
|
|
|
|
Orangy Tang
JGO Kernel      Posts: 2959 Medals: 37
Monkey for a head
|
 |
«
Reply #18 on:
2003-08-13 08:30:16 » |
|
One of the biggest reasons for having const is passing arguments by const reference. Without pass by reference in Java, const loses a lot of use. Deftly stepping around the whole 'Java passes everything by value' argument, how is the C++ code: void Class::SomeFunc(Vector2f* position); any different from Java: void someFunc(Vector2f position); ? This is another area that i feel like const would be handy - I don't want to pass in the actual object because i don't want it changed, yet making a copy is a waste of time. A simple const modifier is exaxtly whats needed (could this be a compile time check, and thus be added to the language without too much fuss?). Instead of the nice simple void someFunc(const Vector2f position); I end up with the rather more ugly: void someFunc(float x, float y); Even Orangy Tang's example is usually not done quite that way in C++, usually a const reference is returned. Well really its not what i usually come across in C++ code, its usually something like void Class:GetPos(Vector2f* pos); which is nice because the caller can just create the object on the stack and let it quietly go out of scope without worrying about a GC sticking its neak in.... Edit: its not a simple compile time modification, you can't tell if a given method will modify the object or not until runtime.
|
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #19 on:
2003-08-13 09:07:28 » |
|
Deftly stepping around the whole 'Java passes everything by value' argument, how is the C++ code: void Class::SomeFunc(Vector2f* position); any different from Java: void someFunc(Vector2f position);
Generally they're not that different. In both cases you're passing in a copy of the pointer/reference to the object. Since it's a new copy, you can't cause the original pointer/reference to refer to a new object. But that's a C++ pointer, not reference. with void foo(SomeObject &bar); you've passed in the actual object via reference. This method has free reign on this object, including causing it to be an entirely new object altogether. Sometimes you want that sometimes you don't. void foo(const SomeObject &bar); Same thing, but bar can't be altered in anyway. No expensive copying, you get the object to work with and it's safe. Everyone's happy. This is essential when templates come into play template <class T> void foo(const T &bar); that T could be a lowly byte to a gigantic object. Since you don't know, you really don't want to copy it, const reference passing becomes crucial. And of course, being C++, there's about a billion other combinations, const pointers, pointer references, pointers to pointers, on and on. Well really its not what i usually come across in C++ code, its usually something like void Class:GetPos(Vector2f* pos); which is nice because the caller can just create the object on the stack and let it quietly go out of scope without worrying about a GC sticking its neak in....
Why though? just do const Vector2f &getPos() const; no object creation takes place at all, and everyone gets a loot at the object in a safe way.
|
|
|
|
|
Orangy Tang
JGO Kernel      Posts: 2959 Medals: 37
Monkey for a head
|
 |
«
Reply #20 on:
2003-08-13 09:15:59 » |
|
I think you're missing my point somewhat - I'm perfectly happy with the myrad of options that C++ presents, but rather miffed that providing similar safety in Java involves a large amount of tedious leg work 
|
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #21 on:
2003-08-13 09:26:14 » |
|
You presented generally poor C++ code and asked why it wouldn't be done that way, I answered. Adding C++ style const to Java wouldn't be a simple addition, and it's debatable how useful it'd even be considering how the rest of Java works. Edit: its not a simple compile time modification, you can't tell if a given method will modify the object or not until runtime.
C++ compilers do it all the time, they aren't involved in the runtime process at all.
|
|
|
|
|
Orangy Tang
JGO Kernel      Posts: 2959 Medals: 37
Monkey for a head
|
 |
«
Reply #22 on:
2003-08-13 09:42:25 » |
|
Most of the snippits are examples that i've seen in other C++ libs, mainly MFC, I'll be the first to admit that my C++ knowledge is not as complete as i'd like.
As for compile time const checking, C++ compilers don't have to deal with dynamically loaded classes.
|
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #23 on:
2003-08-13 10:10:30 » |
|
(and I think we're officially off topic now  ) I don't see what dynamically loading the classes has to do with compile time const checking. For one most C++ implementations have support for dynamically loaded classes/libraries, it's just not universal like Java's is. I'd imagine it's a matter of adding an additional flag to the symbol table, and checking the flag at each assignment operation. Or there's probably a better way as that'd likely be expensive. But if all classes come from the same compiler, then I don't see how the fact that they're dynamically loaded matters. C++'s const checking is far from perfect. Like I said earlier, you can explicitally cast away constness (something the compiler's aware of and is ok with) or it's also possible to sneak constness off in ways the compiler doesn't realize. In the end the developer still has to have a head on his shoulders  All these things like const are just there to help, not be absolute.
|
|
|
|
|
Orangy Tang
JGO Kernel      Posts: 2959 Medals: 37
Monkey for a head
|
 |
«
Reply #24 on:
2003-08-13 10:22:12 » |
|
Well, as an example: I'm using an imaginary lib that has Vector2f, which has a .angleTo(Vector2f) call. Now in a function i'm using what is supposed to be a const vector, and since the angle function returns the angle and leaves the vector alone all is good.
But then on another system the program is run, and the same lib is using a different version which (for reasons unknown) normalises the vectors while calculating the angle - the const object has been changed and theres no way for the compiler to pick up on it.
|
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #25 on:
2003-08-13 11:55:54 » |
|
The interface to his method needs to declare that. He either accepts a const vector or a nonconst vector, and that generally indicates if the object could or won't change. If his interface declares a nonconst argument, it's an error to send a const argument to that method, either runtime or compile depending on when it could be caught.
It's sort of like using say java.awt.Color.RED in Java 1.3. That field doesn't exist, it's a compile time error. But if you took a 1.4 class that uses Color.RED in a 1.3 JVM, it becomes a runtime error and the jvm properly catches it. It'd (I assume) do the same for const.
But again, const is just the language/compiler trying to help out, it's not absolute. If you're working with some unknown library that accepts a const vector, for all you know he had to write in some kind of kludge in that method which removes the constness and changes your vector. Maybe he did that to fit his own needs and didn't consider other users. That's really terrible programming, but it happens.
|
|
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #26 on:
2003-08-13 12:08:12 » |
|
What would a const system do to reflection, jni and final?
|
|
|
|
|
cfmdobbie
JGO Wizard     Posts: 1257
Who, me?
|
 |
«
Reply #27 on:
2003-08-13 12:11:43 » |
|
But then on another system the program is run, and the same lib is using a different version which (for reasons unknown) normalises the vectors while calculating the angle - the const object has been changed and theres no way for the compiler to pick up on it. Couldn't you make the const-ness of the arguments and return types an integral part of the method signature? Also, allow subclasses to offer the same or a more strict version of method args, and the same or less strict return values? Hrm, actually I'm not sure that would be so useful. Needs some thought... Yes, but then again... so it goto...  Yeah, can't *wait* for that to be added!  1
| String whatAbout = "String " + "concanenation?"; |
That's very well known, a specific case, and any confusion created is *totally* outweighed by its usefulness. I disagree with user-generated operator overloading because people start creating things like: 1 2 3 4
| boolean result = "Lesser" < "or Greater?" ; Socket s = socket1 + socket2 ; HostName host ; PortNumber port ; URL url = host + port + "/index.html" ; Texture multiTexture = texture1 & texture2 ; |
You give people an utterly misusuable tool, and they'll misuse it utterly! You know they do!
|
Hellomynameis Charlie Dobbie.
|
|
|
tortoise
Full Member   Posts: 230
<3 Shmups
|
 |
«
Reply #28 on:
2003-08-13 12:19:52 » |
|
You give people an utterly misusuable tool, and they'll misuse it utterly! You know they do!
And operator overloading can be used very elegantly, and improve efficiency to boot. It's a double edged sword, no doubt.
|
|
|
|
|
Preston
Sr. Member   Posts: 346 Medals: 2
|
 |
«
Reply #29 on:
2003-08-14 06:45:40 » |
|
On http://developer.java.sun.com/developer/community/chat/JavaLive/2003/jl0729.html the same question like here as been asked (and answered by a SUN engineer) : Q: Is there any plan to support the const keyword in the future, as in C++? If not, is there a reason?
Josh Bloch: We do not have plans to add support for the const keyword to the Java language. It was a mixed blessing in C++; as you know, it's merely advisory, and can cast on or off. Gosling hated it, and did final instead. What you really want is "immutable," but it's a research problem to make this work properly.
|
Memento mori.
|
|
|
|