Java Forum / General / June 2007
Anonymous class - I don't know how to...
kaja_love160@yahoo.com - 28 Jun 2007 18:35 GMT hello
1) Syntax for creating anonymous class:
new < superclass> ( <params> ) { <class definition> };
Example:
Object o = new Object() { public int anon = 20; };
The above statement creates an object of anonymous class and returns a reference to it. But in order to use members declared in anonymous class ( o.anon ), variable o would have to be of same type as anonymous class. How do we make 'o' of same type, considering that anonymous class has no name?
2) If we declare class C inside method A(), then any local variable ( declared inside A() ) that will also be used inside class C, must be declared final. Why? I assume there must be some valid reason for that?
public void A ( final int i ) {
class C { int u = i ; } }
thank you for your help
Andreas Leitgeb - 28 Jun 2007 20:40 GMT > hello > 1) Syntax for creating anonymous class: [quoted text clipped - 5 lines] > a reference to it. But in order to use members declared in anonymous > class ( o.anon ), ... ... you'd not only define the variable, but also overwrite a (non-final, of course) method from the base-class, which then accesses the variable.
e.g. Object o = new Object() { public int anon = 20; // doesn't make much sense except for the sake of an example: public String toString() { anon++; return Integer.toString(anon); } } o.toString(); // -> 21 o.toString(); // -> 22
Alternatively, you can also use the reflection-API to see and access the variable "anon" of the object referenced by "o".
> 2) > If we declare class C inside method A(), then any local variable [quoted text clipped - 4 lines] > class C { int u = i ; } > } The reasons are perhaps more technical than anything else. At class-file level, C is in a separate class-file than the class containing A, and it would be quite complicated to implement a connection between C's "i", and the local variable "i" in A. The same restriction holds currently, if "i" were a field of the class that contains "A", and my feeling is, that this case is slightly more likely to change in future than the case of a local variable.
In a nutshell, I consider it a current limitation of implementation that was then taken into java language spec for practical reasons. I wouldn't bet that this requirement will be there into all eternity. At some point in future, some brilliant programmer might find a way to actually implement a live connection between these vars, despite the current fundamental difference between local variables and class variables, and the requirement for "final" might then be dropped.
PS: I'm sure, some will followup with their personal decision, that they indeed would bet on this limitation never ever changing, and I wouldn't bet against them, either. I'm not really the betting type :-)
Twisted - 28 Jun 2007 23:00 GMT On Jun 28, 3:40 pm, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at> wrote:
> kaja_love...@yahoo.com <kaja_love...@yahoo.com> wrote: > > hello [quoted text clipped - 40 lines] > slightly more likely to change in future than the case of a local > variable. You can work around it too, by using final arrays or final objects with mutable fields. In fact, you can get closure-like behavior:
public int lexicalContext () { final int[] someLocal = new int[1]; someLocal[0] = 17; final int[] result = new int[1]; ... Foo closure = new Foo () { public double evaluate (int g) { // do stuff with g and someLocal[0] someLocal[0]++; // do more stuff if (foobar) return 1.0; // returns to evaluator if (somethingElse) { result[0] = 42; throw new MyClosuresRuntimeException(); // returns from lexicalContext(!); see below } ... return g*someLocal[0]*Math.PI/11; } } ... try { int fiddlesticks = mumble.evaluator(closure); // may invoke closure, which may return from here // or back into mumble.evaluator ... // do something with fiddlesticks ... return fiddlesticks%3 } catch (MyClosuresRuntimeException e) { return result[0]; // and yes I can also pull // a rabbit out of a hat in case you were wondering } }
Andreas Leitgeb - 29 Jun 2007 14:30 GMT > You can work around it too, by using final arrays or final objects > with mutable fields. In fact, you can get closure-like behavior: It's good that you mentioned the use of arrays for sharing non-final data with a closure. I plainly forgot to mention this workaround, myself.
However, I didn't understand the necessity of involving exceptions. whenever the closure has done its job, you just access the array element in the outer object, no matter if any exceptions were thrown from the closure or whether it returned normally.
Even more, if you pass the closure to somewhere, where it is supposed to be "used" more than once, then your exception-throwing closure will break that after its first use.
Surely there are cases, where you want to throw exceptions from the closure, but that isn't at all related to sharing data.
Twisted - 29 Jun 2007 21:49 GMT On Jun 29, 9:30 am, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at> wrote:
> > You can work around it too, by using final arrays or final objects > > with mutable fields. In fact, you can get closure-like behavior: [quoted text clipped - 14 lines] > Surely there are cases, where you want to throw exceptions > from the closure, but that isn't at all related to sharing data. The exception is to do the other thing closures are supposed to be able to do -- return from the enclosing function. In Smalltalk you can just [^value] inside a block, and the return doesn't simply exit the block, it exits the function with the block literal inside it and returns the value specified. To emulate this in Java you would throw a runtime exception from a method of an anonymous inner class, and catch it in the method that passes it to another call.
Yes, if it gets stored and used later, after the original method has exited, the exception will bomb the JVM. So? That's a bug -- in Smalltalk, it is likewise a bug if you store a [^value] somewhere and eventually execute it after the original context has already returned. Bugs like these are supposed to result in a stack trace from some uncaught exception in Java, as with NullPointerException and the like. :)
In practise, I don't see much use for return-from-context capability in Java, versus its handiness in something like Smalltalk. The interesting thing is that it is actually there, if obscure and demanding some syntactic salt. (It's also not as robust, in that bad code that catches and discards arbitrary RuntimeExceptions in the call chain can break it. Then again, Smalltalk closures are non-reentrant! Threading in Java is so much less painful. :))
Patricia Shanahan - 28 Jun 2007 23:08 GMT ...
>> 2) >> If we declare class C inside method A(), then any local variable [quoted text clipped - 13 lines] > slightly more likely to change in future than the case of a local > variable. I don't understand the remark about "a field of the class...".
This program:
public class AnonTest {
private String name = "InitialName";
public void setName(String newName){ name = newName; }
public Object o = new Object(){ public String toString(){ return name; } };
public static void main(String[] args) { AnonTest a = new AnonTest(); System.out.println(a.o); a.setName("xxx"); System.out.println(a.o); } }
compiles and runs. Could you modify it to show the nature of the restriction?
Patricia
Andreas Leitgeb - 29 Jun 2007 14:17 GMT >> The same restriction holds currently, if "i" were a field of the >> class that contains "A", ... > I don't understand the remark about "a field of the class...". > Patricia You're right. It was a bug in my thinking...
If "i" were a field of the class that contains "A", ... then it of course *doesn't* need to be final.
Sorry for confusion caused by that statement.
Stefan Ram - 28 Jun 2007 23:15 GMT >>final. Why? >The reasons are perhaps more technical than anything else. »Guy Steele wrote:
Actually, the prototype implementation *did* allow non-final variables to be referenced from within inner classes. There was an outcry from *users*, complaining that they did not want this!«
http://madbean.com/2003/mb2003-49/
Twisted - 29 Jun 2007 00:49 GMT > >>final. Why? > >The reasons are perhaps more technical than anything else. [quoted text clipped - 7 lines] > > http://madbean.com/2003/mb2003-49/ And they got it anyway. See elsewhere in this thread -- Java effectively *does* have closures, via the one-element-array trick and exception abuse.
(Storing the closure and executing it after the enclosing function exited of course results in a runtime exception bubbling up elsewhere in the code and probably blowing everything up, but that's only proper -- closures that return from their enclosing function are not meant to be stored and executed after that function returns, and doing this anyway is a bug and *should* therefore blow up the application with a runtime exception stack trace as its final testament, just as invoking a method on null does. The major issue is that really the exception should be unique to each instance of the enclosing function being executed. This can in effect be done, if you make a RuntimeException in the enclosure:
public int enclosure () { final RuntimeException throwme = new RuntimeException(); final int result[] = new int[0]; ... Runnable closure = new Runnable () { public void run () { ... if (someCondition) { result[0] = something; throw throwme; } ... } } ... try { doSomethingWith(closure); } catch (RuntimeException e) { if (e == throwme) return result[0]; throw e; } }
The specific exception constructed for this invocation of enclosure is compared for identity with the caught exception. If it's not the exact same object the exception is simply rethrown, so the enclosure method does not improperly eat any exception that should be handled elsewhere or abort the program. Including even a closure returning from lexical scope after its embedding function had already returned. Including even the closure in "enclosure" doing so, and then trying to return, and this managing to happen inside the try block during a later call to "enclosure".
Of course, you'll probably not believe me if I tell you that Java also has optional method parameters with defaults and keyword syntax. Right? I knew it! You don't believe me.
public void function (Map args) { Integer numFoos = (Integer)(args.get("numFoos")); if (numFoos == null) numFoos = (Integer)(defaults.get("numFoos")); int nFoos = numFoos.intValue(); Runnable doOnFail = (Runnable)(args.get("doOnFail")); if (doOnFail == null) doOnFail = (Runnable) (defaults.get("doOnFail")); Integer maxFoos = (Integer)(args.get("maxFoos"));... ... if (nFoos > mxFoos) doOnFail.run(); ... }
Map futz = new HashMap (); futz.put("numFoos", Integer.valueOf(3)); function(futz);
Still don't believe me? :)
Andreas Leitgeb - 29 Jun 2007 13:21 GMT >>>final. Why? >>The reasons are perhaps more technical than anything else. > http://madbean.com/2003/mb2003-49/ Actually, I interpret this comment by Guy Steele to affirm my position, except for one small detail:
It wasn't (as I implied) that no way was found to do this connection, but the way he (Guy Steele) had implemented had some disadvantages that the users didn't want to accept, back then.
Even Guy Steele seems to believe in a chance to eventually "... restoring full-blown support for closures in the future." even for his implementation, where I only speculated about *some future* implementation.
PS: full blown support for closures means direct support, not just with help of explicit "containers/wrappers".
Twisted - 29 Jun 2007 21:56 GMT On Jun 29, 8:21 am, Andreas Leitgeb <a...@gamma.logic.tuwien.ac.at> wrote:
> PS: full blown support for closures means direct support, > not just with help of explicit "containers/wrappers". All that they need to do is write in some syntactic sugar for the stuff I demonstrated in this thread. For the passing-in-mutable-locals thing, silently autoboxing them in one-element arrays -- not unlike the autoboxing of primitives put into container classes or otherwise used with generics in 1.5. A return-from-context capability could use an anonymous exception throw and catch under the hood, not allow the exception to be absorbed by intervening code (even if it has "catch (Exception e) {...}"; of course "finally" clauses would still execute as for any exception propagating up the call chain), and provide a simple syntax for returning from the enclosing method, say methodname.return foo or to disambiguate Classname.methodname.return foo (in case you've decided to get baroque and nest anonymous inner classes, and from a deep down one return from any of the multiple enclosing contexts).
In practise, I expect the requirement for "final" on the local variables may be quietly dropped and nothing will be done to support returning from enclosing contexts, though it could be done, as it just isn't very useful in typical Java programming (to the point that I've never used it aside from as proof-of-concept).
Tom Hawtin - 30 Jun 2007 15:38 GMT > In practise, I expect the requirement for "final" on the local > variables may be quietly dropped and nothing will be done to support > returning from enclosing contexts, though it could be done, as it just > isn't very useful in typical Java programming (to the point that I've > never used it aside from as proof-of-concept). If closures could support abstraction like the for loop, early exits should be common. Think how you would normally implement a 'find first'.
OTOH, if closures are supposed to abstract control flow away, then the early exits should be moved to the 'algorithm method' and not in the closure.
Tom Hawtin
Mark Space - 28 Jun 2007 22:19 GMT > Example: > [quoted text clipped - 5 lines] > anonymous class. How do we make 'o' of same type, considering that > anonymous class has no name? To re-enforce what Andreas said, this is a design issue, not a language issue. You must design your super-class so that you can access variables or other information from the anonymous class.
If you absolutely cannot, then here's the reflection example:
package anontest; import java.lang.reflect.Field;
public class Main { public static void main(String[] args) throws IllegalAccessException { Object o = new Object() { public int anon = 20; }; printAnon( o );
} static void printAnon( Object o ) throws IllegalAccessException { Class c = o.getClass(); Field fields[] = c.getDeclaredFields(); for( Field f : fields ) { System.out.println( f.getName() + " = " + f.get( o ) ); } } }
Better to NOT use Object and just design an appropriate super-class however.
Tom Hawtin - 28 Jun 2007 22:42 GMT > To re-enforce what Andreas said, this is a design issue, not a language > issue. You must design your super-class so that you can access > variables or other information from the anonymous class. > > If you absolutely cannot, then here's the reflection example: And if you don't want to use reflection, but do want to do so really quite odd:
http://jroller.com/page/tackline?entry=method_local_methods_for_recursion
public static void sort(final long[] array) { if (array.length <= 1) return; new Object() { private void sort(int off, int len) { ... partition ...
// Recursively sort non-partition-elements if ((s = b-a) > 1) sort(off, s); if ((s = d-c) > 1) sort(n-s, s); } private void swap(int a, int b) { long aOrig = array[a]; array[a] = array[b]; array[b] = aOrig; } }.sort(0, array.length); }
Tom Hawtin
Mark Space - 28 Jun 2007 23:20 GMT > And if you don't want to use reflection, but do want to do so really > quite odd: > > http://jroller.com/page/tackline?entry=method_local_methods_for_recursion Tom you've hurted my brain again.
Patricia Shanahan - 28 Jun 2007 22:55 GMT >> Example: >> [quoted text clipped - 9 lines] > issue. You must design your super-class so that you can access > variables or other information from the anonymous class. More usually, it is not even a matter of designing a super-class. The commonest use of anonymous inner class objects is to provide a very simple, one-off, implementation of some interface, such as Comparator.
Patricia
Mark Space - 28 Jun 2007 23:10 GMT > More usually, it is not even a matter of designing a super-class. The > commonest use of anonymous inner class objects is to provide a very > simple, one-off, implementation of some interface, such as Comparator. I almost gave an example of a "properly designed" super-class using Runnable as the super-class, but in both cases I think Comparator and Runnable are super-classes that have already been designed for the programmer. This was sorta my point.
If one is going to start with Object, then one has to do a bit more work to get the proper super-class. The question is: does an already designed class exist, or does the programmer have to invent one?
I don't like the idea of overriding toString(); this changes the implied purpose of the method. It's fine for the quick example Andreas gave, but in the real world it would invite ridicule. In general, I'd think most classes would not have an appropriate method to override unless one was designed in, such as the compareTo() method Comparator provides.
Roedy Green - 29 Jun 2007 00:56 GMT > How do we make 'o' of same type, considering that >anonymous class has no name? The whole point of an anonymous class is you only need to create them at one point in all your code. Otherwise use an inner class.
If you want to be silly you can use tricks described at http://mindprod.com/jgloss/classforname.html to get a Class object then use reflection to instantiate more of them. -- Roedy Green Canadian Mind Products The Java Glossary http://mindprod.com
Mike Schilling - 29 Jun 2007 04:21 GMT > hello > [quoted text clipped - 11 lines] > anonymous class. How do we make 'o' of same type, considering that > anonymous class has no name? You use some other sort of inner class; that's not what anonynmous classes are for.
> 2) > If we declare class C inside method A(), then any local variable [quoted text clipped - 8 lines] > } > } Presumably to help answer to the question "When the anonymous (or local) class instance accesses a local variable, does it see the current value or the value as it was when the instance was created?" If the variable is final, the answer is "Both".
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 ...
|
|
|