Java Forum / General / April 2008
enumerate the consumers of foo.toString() within an application
Rex Mottram - 19 Apr 2008 20:32 GMT Say I have a class with an explicit toString() method, for which I want to change the output format (have it return a different String). I first want to see if anywhere else in the program is using it and might be broken (this is not a published API but a standalone application - otherwise I wouldn't change toString at all).
This turns out to be harder than it I thought. For one thing, toString is special in that it can be called implicitly, i.e. out.println(foo) is the same as out.println(foo.toString()). So I can't depend on searching for literal occurrences of foo.toString. With another method I could remove or rename it temporarily and see who else breaks. But of course toString will just revert to the superclass.
The meta-problem is that I often provide a toString as a debugging aid only - for instance Eclipse will show the toString result for any highlighted instance which can be very handy. Other times it's doing something critical. I guess one lesson of this is that toString is best left as a convenience/debugging aid and more descriptive method names be used for mission-critical stringifying. But in the meantime, any bright ideas on how to enumerate the users of foo.toString() within a self-contained application?
RM
Stefan Ram - 19 Apr 2008 20:47 GMT >Say I have a class with an explicit toString() method, for which I want >to change the output format (have it return a different String). You can write toString() as you like it.
>This turns out to be harder than it I thought. For one thing, toString >is special in that it can be called implicitly, i.e. out.println(foo) is >the same as out.println(foo.toString()). It is not strictly the same - it just might have the same effect.
»toString()« is not special compared to any other method.
>ideas on how to enumerate the users of foo.toString() within a >self-contained application? You need to inspect all libraries you use for uses, too, because of run-time polymorphism in Java. Let me elaborate ...
What is object-oriented programming?
Object-oriented programming uses objects (storage entities) annotated with run-time types: Every object carries information about its type, which implies information about the layout and encoding of its storage.
What is an object-oriented programming language?
An object-oriented programming language supports object-oriented programming, but it usually hides the type information of objects and automatically assigns operations specified for a type to each object. Thus, in an object oriented programming language, the programmer does not read the type information himself nor does he use it to select an appropriate action. Instead he just states the action by a "verb" with a type and the programing language will then make sure, that the correct implementation is called for any given object at run-time. This is called »run-time polymorphism« or »late binding«. Individual object-oriented programming languages might include additional support for object-oriented programing, but this binding is the core feature of every object-oriented programming language.
What is the main advantage of an object-oriented programming language?
The main advantage is that verbs can be extended without the need to modify existing code. For example, in Java, a new type can extend the verb »toString«. This extension can then be used immediatly by the existing »println« verb, without the need that the author of the code for »println« was aware of the new type, so it is not necessary to modify the given Java SE library, which contains other definitions of the verb »toString«. One only needs to /add/ new code. Thus, the »open/closed principle« is fulfilled.
Can you give a small example programm that shows something that can be done in an object-oriented programming language, but is not possible in a purely procedural programming language?
Yes, a Java program illustrating the answer to the previous question is:
class Position { int x; int y; public java.lang.String toString() { return java.lang.String.valueOf( x ) + ", " + java.lang.String.valufOf( y ); }}
public class Main { public static void main( final java.lang.String[] args ) { java.lang.System.out.println( new Position() ); }}
The output of this program is as follows.
0, 0
What is the reason for run-time information?
It is needed whenever data, whose encoding, layout or language might change or is not yet known, is to be transfered between two parties to indicate this encoding, layout or language.
Roedy Green - 19 Apr 2008 21:40 GMT > »toString()« is not special compared to any other method. Except that Sun code will call toString under a number of circumstances. In that sense it is magic.
 Signature
Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Stefan Ram - 19 Apr 2008 21:44 GMT >>»toString()« is not special compared to any other method. >Except that Sun code or code form other parties
> will call toString under a number of >circumstances. In that sense it is magic. Yes, as magic as »hashCode()« and other methods of »java.lang.Object« or other superclasses or interfaces (like java.lang.Runnable#run()).
Eric Sosman - 19 Apr 2008 22:04 GMT >> »toString()« is not special compared to any other method. > > Except that Sun code will call toString under a number of > circumstances. In that sense it is magic. Could you elaborate? Off-hand, I can't think of any circumstances where the compiler fabricates a toString() call out of thin air. Something to do with Enum, maybe?
 Signature Eric Sosman esosman@ieee-dot-org.invalid
Bill Butler - 19 Apr 2008 22:43 GMT >>> »toString()« is not special compared to any other method. >> [quoted text clipped - 4 lines] > circumstances where the compiler fabricates a toString() > call out of thin air. Something to do with Enum, maybe? System.out.println(foo); // this calls foo.toString() to get a String from an object
Granted...this is not really fabricating anything, but it appears magical at first glance
Bill
Eric Sosman - 20 Apr 2008 01:54 GMT >>>> »toString()« is not special compared to any other method. >>> Except that Sun code will call toString under a number of [quoted text clipped - 5 lines] > System.out.println(foo); // this calls foo.toString() to get a String > from an object But the toString() call is not magically generated by the compiler at all. The compiler generates a call to the PrintStream.println(Object) method, which in turn makes a perfectly normal call to String.valueOf(Object), which in its turn calls toString() on foo's class. No magic, just explicit calls to perfectly ordinary methods.
However, I've thought of one sense in which toString() may be thought of as magical: String concatenation. In
String s = myDog + " is bigger than " + yourDog;
the compiler "magically" calls toString() on the StringBuilder object that actually performs the concatenation. Note that it does not call myDog.toString() nor yourDog.toString(); rather, it calls StringBuilder.append(xxxDog), which eventually gets around to calling xxxDog.toString().
Still, it might be considered magical that the compiler knows StringBuilder.toString() is useful.
 Signature Eric Sosman esosman@ieee-dot-org.invalid
Peter Duniho - 20 Apr 2008 02:34 GMT >>>>> »toString()« is not special compared to any other method. >>>> Except that Sun code will call toString under a number of [quoted text clipped - 7 lines] > But the toString() call is not magically generated by the > compiler at all. Note, however, that the original statement said nothing about the compiler. Simply that toString() will be called. (It said by "Sun code", but it's not limited to that, especially since not all Java implementations were written by Sun :) ).
The real question is whether there are situations in which toString() is called implicitly and thus could be considered "special". And there are. I think in that respect, it is "special". Though, it's only "special" to the extent that any method in the base Object class is "special" and thus relied upon by a variety of code using those methods.
Pete
Bill Butler - 20 Apr 2008 14:25 GMT <snip>
>> But the toString() call is not magically generated by the >> compiler at all. [quoted text clipped - 10 lines] > is "special" and thus relied upon by a variety of code using those > methods. Well said Peter, There are no "rules" being broken, by the compiler, on behalf of the toString method. There are places, however, where the code takes advantage of the fact that every Object must have a "toString" method. This can result in code that can appear magical at first glance.
Bill
Lew - 20 Apr 2008 14:48 GMT > There are places, however, where the code takes advantage of the fact > that every Object must have a "toString" method. > This can result in code that can appear magical at first glance. So can polymorphism generally. The code relies on the fact that all concrete types inherit from Object. That's pretty much fact number one about Java, isn't it? Expressing behavior polymorphically is arguably the core idiom of object-oriented programming.
Done right, it is magic.
 Signature Lew
Rex Mottram - 20 Apr 2008 19:59 GMT >> There are places, however, where the code takes advantage of the fact >> that every Object must have a "toString" method. [quoted text clipped - 6 lines] > > Done right, it is magic. But not all _methods_ inherit from Object methods. Those that do get an extra helping of magic.
BTW, not that I mind the interesting debate at all but for the record please note that I (the OP) did not use the word "magic". Nor "unique", nor "uniquely magical". I said "special", and it is. Apparently because the compiler know that it inherits all the way from Object.
RM
Lew - 20 Apr 2008 20:29 GMT >>> There are places, however, where the code takes advantage of the fact >>> that every Object must have a "toString" method. [quoted text clipped - 9 lines] > But not all _methods_ inherit from Object methods. Those that do get an > extra helping of magic. Yes, that magic is called "polymorphism", and it is used every time a class inherits an instance method.
Every method that inherits from a superclass gets that "helping of magic"; there's nothing "extra" about it.
> BTW, not that I mind the interesting debate at all but for the record > please note that I (the OP) did not use the word "magic". Nor "unique", > nor "uniquely magical". I said "special", and it is. Apparently because > the compiler know that it inherits all the way from Object. It isn't special, it's normal polymorphism.
 Signature Lew
Mark Space - 21 Apr 2008 04:25 GMT > BTW, not that I mind the interesting debate at all but for the record > please note that I (the OP) did not use the word "magic". Nor "unique", > nor "uniquely magical". I said "special", and it is. Apparently because > the compiler know that it inherits all the way from Object. The only magic is in the way string objects can be created without "new" or by concatenation with an overloaded "+".
All the Sun code does is:
class Test { public void someMethod( Object o ) { o.toString(); //... } }
That's it. There's no magic or preferential treatment by the compiler that I can see. It's not special. It's plain old bog standard object inheritance. You can do the exact same thing yourself.
Mark Space - 19 Apr 2008 21:34 GMT > highlighted instance which can be very handy. Other times it's doing > something critical. I guess one lesson of this is that toString is best > left as a convenience/debugging aid and more descriptive method names be > used for mission-critical stringifying. But in the meantime, any bright Yes, and good design would have spotted that fairly early. So the other meta issue is why did this requirement not get captured properly? Something to look at for future reference and learning and best practices and such.
My advice, although it may be painful: Since you have to search, manually, for all occurrences of this overloaded (I mean design-wise, not overloaded in the programming/Java sense of the word) use of toString(), you should just refactor the design. Add the new method now to the class(es) and replace the use of toString() with the new method where you find it is needed.
Logan Shaw - 19 Apr 2008 21:49 GMT > Say I have a class with an explicit toString() method, for which I want > to change the output format (have it return a different String). I first > want to see if anywhere else in the program is using it and might be > broken (this is not a published API but a standalone application - > otherwise I wouldn't change toString at all). Some issues have already come up in this thread. One is how far your toString() might be visible (i.e. are you writing library code). You implied you don't care about that, nor do you care about complications with subclasses and so on.
Anyway, a couple of thoughts:
(1) You can't remove toString() or modify its signature, but you could temporarily modify toString() to make it throw an unchecked exception. Now, if you can only get all the code in the project to run, you could detect who's calling toString(). That may be difficult for all the reasons that code coverage (and covering all the relevant inputs) can be, but then again maybe not.
(2) Won't these toString() calls have to be explicit in the bytecode? Could you analyze the .class files instead of the source code?
(3) In the idealized unit test world, unit tests would notice all the breakages you caused by changing toString(), and they would provide a helpful safety net.
It strikes me that even though #2 could get you a lot closer, it can't do easily do anything about "Object o = getSomeObject; o.toString();". Maybe some static flow analysis (on the .class files, yay) could, but then again probably not completely, and the type erasure of generics unfortunately may make that worse. So leveraging the compiler's habit of making toString() explicit in the .class files is good for one reason, but it's bad since the compiler throws out other information you need. Bleah. :-)
- Logan
Eric Sosman - 19 Apr 2008 22:13 GMT > [... finding implicit and explicit toString() calls ...] > (2) Won't these toString() calls have to be explicit in the bytecode? > Could you analyze the .class files instead of the source code? I don't think that will work. In a situation like
Thing someThing = new Thing(); ... System.out.println(someThing);
... there will be no toString() call in the class file, just an ordinary call on System.out.println(Object) -- which will eventually get back to Thing.toString() via an invokevirtual.
String meAlong = someThing + ":" + zipCode;
... the bytecode will have one call on StringBuilder.toString(), but no calls to Thing.toString(). There will be ordinary calls to StringBuilder's overloaded append() methods, and one of those will eventually get back to Thing.toString(), but you won't find that toString() call in the snippet's bytecode.
 Signature Eric Sosman esosman@ieee-dot-org.invalid
Roedy Green - 19 Apr 2008 23:30 GMT >first >want to see if anywhere else in the program is using it and might be >broken (this is not a published API but a standalone application - >otherwise I wouldn't change toString at all). An IDE like Intellij will find uses for you, but since the method could also be called via Object.toString the number of all possible uses would be overwhelming.
 Signature
Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
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 ...
|
|
|