"I really wanna change the behavior of a class method without changing the class, or encapsulating it, or extending it with inheritance" said a group of people that included every black hat hacker in history.
The Java language checked its fundamental security model. "Nope" said Java. "You can never, ever, ever, ever do that. Ever."
Although not very recommended for this particular case, it is nevertheless very doable. ;;)
However, the @OverridetoString() will kick in only for those PVector objects instantiated by the "hacked" class. L-)
We can't force 3rd-party code to use our modified class in place of the original 1. [-X
That's why some utility function which accepts a PVector instance is more guaranteed to work for all cases than having some derived class which attempts to change the original methods' behavior. :-B
/**
* PVector Subclass Example (v1.0.1)
* GoToLoop (2018/Jan/16)
*
* https://Forum.Processing.org/two/discussion/25986/
* how-can-i-replace-pvector-tostring#Item_7
*/
void setup() {
final PVector orig = PVector.random3D(this).mult(100);
println(orig);
final PVector hack = new MyVec().set(orig);
println(hack);
exit();
}
public static class MyVec extends PVector {
public MyVec() {
super();
}
public MyVec(final float x, final float y) {
super(x, y);
}
public MyVec(final float x, final float y, final float z) {
super(x, y, z);
}
@Override public String toString() {
return "{ x: " + x + ", y: " + y + ", z: " + z + " }";
}
}
We can't force 3rd-party code to use our modified class in place of the original 1.
Fair enough. The bigger issue with this solution is it can't force NON-3rd-party code to use our modified class in place of the original 1. We have to manually replace new PVector by new MyVec.
The core concept here is that, if you recompile Processing (Java) from its source, you can change anything you want, because it is all yours. Redefine PVector and that's it -- done.
However, if you use the version that is already compiled, there are things that by design you just can't change about it -- easily, or at all (short of a hacked JVM / byte ode hacks). Even if you redefine a class, everything in the original code that accepts an original PVector as an argument still relies on and only accepts that original class/type. This is how Java works -- it isn't anything specific to Processing.
Python (and Processing.py) is very, very different in this regard. Not only can you modify classes even at runtime, but most things fundamentally don't care about the types of their method signatures -- everything is duck-typed, so if you shadow or extend PVector (and/or use decorators, modify the class itself at runtime, etc.) and pass it to things, nothing complains. All those approaches just work in Python; in Java none of them work.
Even if you redefine a class, everything in the original code that accepts an original PVector as an argument still relies on and only accepts that original class/type.
Fine. No problem there.
What I'd hoped for though is that PVector would work extended in /my/ code.
Python (and Processing.py) is very, very different in this regard.
Understood. Next time, in Processing I'll start in Python. But that's /next/ time. :)
What I'd hoped for though is that PVector would work extended in /my/ code.
It will "work" if you extend the class, use @Override, and then declare a new PVector by creating a new extended class instance, as per @GoToLoop's example above. Now you have an object, but your class won't interoperate with Processing in the way you want -- specifically, you can't coerce new PVector to mean new myVec. This also means for example you can't coerce createGraphics to generate a myPGraphics, and you often can't coerce a static Class.copy() to copy extended properties of your subclassed object.
If that isn't a problem for you, you should follow his example.
It will "work" if you extend the class, use @Override, and then declare a new PVector by creating a new extended class instance, as per @GoToLoop's example above
That's not "it". That requires PVector constructors to be manually recoded.
A usable workaround, but not a solution to the stated problem.
That is an additional workaround. You have already been given a solution to the problem as stated: modify PVector in the Processing source code and recompile.
Review the thread above for the reasons why this is the solution that fits those criteria.
You now have a thing called a PVector, but nothing in the API of the compiled library will accept it.
Perhaps I'm misunderstanding you, but I'm afraid that's not how classical subclass inheritance works under Java, Python, JS, C++, etc. :-\"
Exactly the opposite: Any function parameter of datatype PVector would accept any derived subclass object of that datatype, including my own MyVec subclass. :)>-
That function would still "think" that parameter is a "pure" PVector instance object though. 3:-O
However, anything which would trigger the parameter's toString() method inside that function would get the MyVec's @Override customized implementation instead of the original PVector's. :ar!
But it's also extremely common to pass a PGraphics instance argument to it instead. @-)
Heck! Even Movie & Capture objects can be passed to PApplet::image() method too! 8-}
B/c all of those subclasses extendsPImage parent class. \m/
Or at least extends another subclass which already extendsPImage and so on. :-B
That's what happens to PGraphics, which is always some other derived subclass of it. :\">
When I stated: "We can't force 3rd-party code to use our modified class in place of the original 1."
I've just meant that their internal PVector would be the original 1 from packageprocessing.core, not our hacked 1. :(|)
Yet, if we have direct access permission to their PVector fields, we can easily re-assign them to use our customized subclass: >:)
Let's say we've got this file named "SomeLibrary.java" w/ the following content:
package i.use.original.pvector;
import processing.core.PVector;
public class SomeLibrary {
public PVector vec = new PVector(1e3f, .7f, -30);
@Override public String toString() {
return vec.toString();
}
}
And now PVector Subclass Example version 2, now extended to use that class SomeLibrary: >-)
/**
* PVector Subclass Example (v2.0.1)
* GoToLoop (2018/Jan/17)
*
* https://Forum.Processing.org/two/discussion/25986/
* how-can-i-replace-pvector-tostring#Item_19
*/
import i.use.original.pvector.SomeLibrary;
void setup() {
final PVector orig = PVector.random3D(this).mult(100);
println(orig);
final PVector hack = new MyVec().set(orig);
println(hack, ENTER);
final SomeLibrary lib = new SomeLibrary();
println(lib); // [ 1000.0, 0.7, -30.0 ]
lib.vec = new MyVec().set(lib.vec);
println(lib); // { x: 1000.0, y: 0.7, z: -30.0 }
exit();
}
public static class MyVec extends PVector {
public MyVec() {
super();
}
public MyVec(final float x, final float y) {
super(x, y);
}
public MyVec(final float x, final float y, final float z) {
super(x, y, z);
}
@Override public String toString() {
return "{ x: " + x + ", y: " + y + ", z: " + z + " }";
}
}
If we access the unmodified SomeLibrary::vec field, it still println(): [ 1000.0, 0.7, -30.0 ]
But once we re-assign it w/ our customized MyVec instance, it now println(): { x: 1000.0, y: 0.7, z: -30.0 }.
You still haven't said why you might want to do this.
Correct. That's because I'm not interested in answers from people who need to know that.
I don't need to know but obviously I am interested in knowing why. Since that is the case you are not interested in anything I might contribute so I won't bother with this discussion anymore. :-q
BTW When you know why someone whats a solution to a problem it can possibly direct the responder to possible / alternative solutions.
@GoToLoop -- thank you for pointing out I was very unclear on that "it" before "interoperate correctly." I just reread it, and it does look confusing -- I'll try to clean that up later. Great summary of subclass examples in Processing, too. I do think (?) that chrisjj already knows how inheritance works in general, and knows how to do it -- he just wants to know if he can redefine PVector as such, and without using inheritance that requires declaring a new instance of a subclass.
As an aside, some things about Processing can make rolling your own subclassing a pain in certain circumstances. For example, periodically someone (like me) wants to subclass PGraphics, and then discovers why createGraphics makes that more difficult than it needs to be.
chrisjj already knows how inheritance works in general, and knows how to do it -- he just wants to know if he can redefine PVector as such, and without using inheritance that requires declaring a new instance of a subclass.
1st of all, how can you expect method add() to be overridden if there's nothing overridden besides method toString() and those 3 overloaded constructors! =P~
It shoulda been very obvious that when we use extends in a class, it means it inherits all of its parent's members unchanged; the exactly way they had been implemented originally. I-)
Only then we have the choice to add more members and/or override, overshadow, overload the previous 1s. :)>-
Another very important matter is that static methods can't be truly overridden; at most overshadowed! @-)
But that's irrelevant for now, b/c you haven't made any attempts to override or overshadow anything, according to the code you had posted above! :-\"
The issue is that in Java, we can assign a subclass object to a variable of 1 of its parent datatypes, but not the opposite. :-SS
That's why PVector v2 = MyVec.add(v, v); is valid, while MyVec v3 = MyVec.add(v, v); isn't. [-X
B/c the static overloaded version of method add() returns datatype PVector, not MyVec. :-B
Unless you wanna bother overshadowing all static methods, like @xxMrPHDxx had shown, lemme teach ya a workaround for it: :ar! MyVec v3 = (MyVec) MyVec.add(v, v, new MyVec());
Or never declare a variable as datatype MyVec. Always go w/ PVector. ;)
Answers
I wouldn't. Instead, I would just write your own function to print the values in a PVector - however you like. Example:
But if you would? :)
I would create a class that extends from PVector and override that function.
Kf
That would require you to replace all references to PVector with myPVector.
It would, yes.
It's about now that we ask you why you want to do this...
This reminds me of a story:
"I really wanna change the behavior of a class method without changing the class, or encapsulating it, or extending it with inheritance" said a group of people that included every black hat hacker in history.
The Java language checked its fundamental security model. "Nope" said Java. "You can never, ever, ever, ever do that. Ever."
The End
Although not very recommended for this particular case, it is nevertheless very doable. ;;)
However, the
@Override
toString() will kick in only for those PVector objects instantiated by the "hacked" class. L-)We can't force 3rd-party code to use our modified class in place of the original 1. [-X
That's why some utility function which accepts a PVector instance is more guaranteed to work for all cases than having some derived class which attempts to change the original methods' behavior. :-B
Fair enough. The bigger issue with this solution is it can't force NON-3rd-party code to use our modified class in place of the original 1. We have to manually replace new PVector by new MyVec.
Also how to do the same for Python Mode: O:-)
comment deleted
comment deleted
kfrajer's solution to extend the class and override the function is the Java / OOP way to do it but
is not entirely true ;)
PVector vec = new MyPVector();
will work but any method in PVector that returns PVector objects should also be overridden to return MyPVector objects.
TfGuy44 provided a suitable alternative for Processing.
You still haven't said why you might want to do this.
The core concept here is that, if you recompile Processing (Java) from its source, you can change anything you want, because it is all yours. Redefine PVector and that's it -- done.
However, if you use the version that is already compiled, there are things that by design you just can't change about it -- easily, or at all (short of a hacked JVM / byte ode hacks). Even if you redefine a class, everything in the original code that accepts an original PVector as an argument still relies on and only accepts that original class/type. This is how Java works -- it isn't anything specific to Processing.
Python (and Processing.py) is very, very different in this regard. Not only can you modify classes even at runtime, but most things fundamentally don't care about the types of their method signatures -- everything is duck-typed, so if you shadow or extend PVector (and/or use decorators, modify the class itself at runtime, etc.) and pass it to things, nothing complains. All those approaches just work in Python; in Java none of them work.
Fine. No problem there.
What I'd hoped for though is that PVector would work extended in /my/ code.
Understood. Next time, in Processing I'll start in Python. But that's /next/ time. :)
Correct. That's because I'm not interested in answers from people who need to know that.
It will "work" if you extend the class, use @Override, and then declare a new PVector by creating a new extended class instance, as per @GoToLoop's example above. Now you have an object, but your class won't interoperate with Processing in the way you want -- specifically, you can't coerce
new PVector
to meannew myVec
. This also means for example you can't coercecreateGraphics
to generate a myPGraphics, and you often can't coerce a staticClass.copy()
to copy extended properties of your subclassed object.If that isn't a problem for you, you should follow his example.
That's not "it". That requires PVector constructors to be manually recoded.
A usable workaround, but not a solution to the stated problem.
That is an additional workaround. You have already been given a solution to the problem as stated: modify PVector in the Processing source code and recompile.
Review the thread above for the reasons why this is the solution that fits those criteria.
Perhaps I'm misunderstanding you, but I'm afraid that's not how classical subclass inheritance works under Java, Python, JS, C++, etc. :-\"
Exactly the opposite: Any function parameter of datatype PVector would accept any derived subclass object of that datatype, including my own MyVec subclass. :)>-
That function would still "think" that parameter is a "pure" PVector instance object though. 3:-O
However, anything which would trigger the parameter's toString() method inside that function would get the MyVec's
@Override
customized implementation instead of the original PVector's. :ar!Another more tangible example is Processing's PImage parent class.
PApplet::image() method's 1st parameter is of datatype PImage:
https://Processing.org/reference/image_.html
But it's also extremely common to pass a PGraphics instance argument to it instead. @-)
Heck! Even Movie & Capture objects can be passed to PApplet::image() method too! 8-}
B/c all of those subclasses
extends
PImage parent class. \m/Or at least
extends
another subclass which alreadyextends
PImage and so on. :-BThat's what happens to PGraphics, which is always some other derived subclass of it. :\">
When I stated: "We can't force 3rd-party code to use our modified class in place of the original 1."
I've just meant that their internal PVector would be the original 1 from
package
processing.core, not our hacked 1. :(|)Yet, if we have direct access permission to their PVector fields, we can easily re-assign them to use our customized subclass: >:)
Let's say we've got this file named "SomeLibrary.java" w/ the following content:
And now PVector Subclass Example version 2, now extended to use that class SomeLibrary: >-)
If we access the unmodified SomeLibrary::vec field, it still println():
[ 1000.0, 0.7, -30.0 ]
But once we re-assign it w/ our customized MyVec instance, it now println():
{ x: 1000.0, y: 0.7, z: -30.0 }
.Hope the subject is much clearer now. ^#(^
I don't need to know but obviously I am interested in knowing why. Since that is the case you are not interested in anything I might contribute so I won't bother with this discussion anymore. :-q
BTW When you know why someone whats a solution to a problem it can possibly direct the responder to possible / alternative solutions.
@GoToLoop -- thank you for pointing out I was very unclear on that "it" before "interoperate correctly." I just reread it, and it does look confusing -- I'll try to clean that up later. Great summary of subclass examples in Processing, too. I do think (?) that chrisjj already knows how inheritance works in general, and knows how to do it -- he just wants to know if he can redefine PVector as such, and without using inheritance that requires declaring a new instance of a subclass.
As an aside, some things about Processing can make rolling your own subclassing a pain in certain circumstances. For example, periodically someone (like me) wants to subclass PGraphics, and then discovers why createGraphics makes that more difficult than it needs to be.
Correct, correct and correct :)
And you can't.
It isn't possible in Java by design, sans hacking JVM, bytecode, possibly classpath(?) etc. as per all above.
Then I assume that means it is also not possible in Processing using Java. Thanks.
I'll try GoToLoop 's MyVec, despite the requirement to redefine all the constructors that's a gross offence to efficiency :)
Those overloaded constructors are the same quantity of those found in the original PVector class: :-@
https://GitHub.com/processing/processing/blob/master/core/src/processing/core/PVector.java#L121-L145
The boilerplate can be a lil' discomforting for the eyes, but they don't slowdown the code. [-(
You may as well place that subclass in another tab. *-:)
I hit a snag. A static function doesn't get the override.
What's the fix/workaround?
You'll need to create your own static add() method that return MyVec inside MyVec class
and use MyVec.add() instead of PVector.add()
That way you can pass both PVector and MyVec object
Thanks but crikey I hope you're wrong. That would mean digging out and pasting the definition of every static vector function that's needed.
extends
in aclass
, it means it inherits all of its parent's members unchanged; the exactly way they had been implemented originally. I-)static
methods can't be truly overridden; at most overshadowed! @-)PVector v2 = MyVec.add(v, v);
is valid, whileMyVec v3 = MyVec.add(v, v);
isn't. [-Xstatic
overloaded version of method add() returns datatype PVector, not MyVec. :-Bstatic
methods, like @xxMrPHDxx had shown, lemme teach ya a workaround for it: :ar!MyVec v3 = (MyVec) MyVec.add(v, v, new MyVec());
I don't expect function add() to be overridden.
I did expect static MyVec.add(v, v) to return a MyVec, inc. the ToString override.
It didn't. Further example:
println(MyVec.add(v, v)); // [ 0.0, 0.0, 0.0 ]
That's truly horrible! ;)
But gives the desired output. Thanks.
PS With added honesty: MyVec v3 = (MyVec) PVector.mult(v, 0, new MyVec());
Well, let's take a look at its actual implementation, shall we? O:-)
https://GitHub.com/processing/processing/blob/master/core/src/processing/core/PVector.java#L501-L508
If parameter target is
null
, makenew
MyVec? Nope, it'snew
PVector! $-)As much as we woulda wished the contrary, Java doesn't automatically replace
new
MyVec w/new
PVector when that method is inherited by MyVec. :-<My workaround simply passes a MyVec object as 3rd argument, so the 3rd parameter target isn't
null
, but contains an actual MyVec instance. :ar!And so at the end, an actual MyVec is returned
return target;
and can be castable to(MyVec)
. >-)Thanks for the explanation.