RFE: Delegate Annotation
I think the annotation is fairly streight forward, however the compiler / processor will need to do some work
import java.lang.annotation.Documented;
importstatic java.lang.annotation.ElementType.METHOD;
importstatic java.lang.annotation.ElementType.FIELD;
import java.lang.annotation.Retention;
importstatic java.lang.annotation.RetentionPolicy.RUNTIME;;
import java.lang.annotation.Target;
/** Delegates implementation of an interface to a member (field/no arg method).
During compilation the containing class will delegate to the instance/result.
In the case where more then one interface defines a method (IE: colission) a compile failure will result.
This compile failure will help enforce proper nameing, and force the developer to handle these situations,
while removing repeditive code.
Also if the member (field/method) does not implemet the desired interface(s), a compile failure will result.
This ensures type safety, while allowing the structure to be generic.
*/
@Documented
@Retention( RUNTIME )
@Target({ METHOD, FIELD} )
public @interface Delegate{
/** A list of <B>interfaces</B> that this member will be used to implement. */
public Class[] value();
}
EXAMPLE
publicclass DelegateTest{
@Delegate( java.lang.Runnable.class )
public Runnable getRunnable();
}
In this example, after compilation, new DelegateTest().run(), is equivalant to (though requires 1 extra call)... new DelegateTest().getRunnable().run()
Let me know if you have any help with this change!
Thanks
Todd_Musheno@yahoo.com
PS: Anyone who has done real world development will see the benifits of such a mechinisum, I have tons of examples if you need some.
[2567 byte] By [
mushenoa] at [2007-10-3 4:19:20]

I have implemented something almost the same as this.
See https://rapt.dev.java.net/nonav/docs/api/index.html?net/java/dev/rapt/exploratory/anon/As.html
The main difference from your desire is that it is designed to work with annotation processing. That is, the generated method is in a separate class as a static method. By carefully choosing the names the code is quite readable.
Sorry, the javadocs at that link are a little gungy.
Click on the Overview link in the API docs for more information.
You can get the jar file from https://rapt.dev.java.net/nonav/servlets/ProjectDocumentList
In terms of an RFE, this intersects with the Closures proposal being worked on presently. Closures are more general purpose than delegates, so a delegates RFE is unlikely to be seriously considered IMHO.
But you can roll your own, or use the rapt one NOW (with Java SE5)
Bruce
This does not solve the solution.
The problem is you have class A that extends B. In scope of A you have Object C that implements interface D, and you want class A to implement D useing Object C to do the work required by D.
Exacly like the example, genericly (not a solution for runnables, a solution for ANY interface).
Please let me know if you find a solution?
Todd
> Exacly like the example
But your example only has a single class, and an annotation, when in fact there are 3 classes and an interface involved. It is hard to understand what you want.
So you want the methods of D added to A, and their body delegates to the corresponding method on C via a reference that A has to a C.
That seems to be what is called a Mixin.
Rapt has a mechanism for that as well. It also has a means to deal with the name collision you discuss.
https://rapt.dev.java.net/nonav/docs/api/index.html?net/java/dev/rapt/exploratory/mixin/package-summary.html
relating your example to the mixin documentation,
A is the Target
B is the Base
D is the Mixin
C is the mixin Implementation.
D and C must be specially coded to work as flavors. A must be annotated to specify B and D. B can be any class.
The code in C can access the instance of A via self(), and can access an instance of B via base() (to be used similar to calling methods on super).
So applying all this to your example this is how you would do it.
You haven't specified where the run() method actually is, so I'll assume it is hereclass C {
public void run() {
System.out.println("hello world");
}
}
Because you want to mixin a Runnable, and we have to code the mixin interface specially, we make a mixinable sub interface of Runnable
@Implementation(C.class)
interface D extends Runnable, Mixin {
public void run();
}
We then modify the implementation so we can use it
class C implements D, MixinImpl<D,Any> {
public void run() {
System.out.println("hello world");
}
}
We then rewrite DelegateTest
@Mix(base=Object.class, add=@Flavor(C.class))
class DelegateTest extends DelegateTest_ {}
When you compile all this with APT, DelegateTest_ is generated, implements Runnable (indirectly via D) and has a run() method that delegates to an instance of C.
Is that what you are after?
Bruce
P.S. I am planning a rewrite of the mixin package to use JSR269, and I've decided to replace the SUX method of implementing generics with a different mechanism that has the generic type coded directly in the annotation but is less like generics syntax.
Yes I am looking for a Delegate, or Mixin (depending on your preference).
However I am not seeing where you are using an internal (method or field) to delegate execution to.
This is a very simple concept, I must be explaining it badly, so bare with me while I ramble on a bit...
Lets say we have an interface "Foo", and we are writing a class "Bar".
Bar needs to implement Foo, but how!
Luckily we happen to have a private instance variable (could just as easly be the result of a method call) "obj" that implements Foo, and does exactly what Bar should do when its methods (at least as defined by Foo) are called.
How do I make that happen as cleanly as possible (you are suggesting the term "Mixin" instead of "Delegate", and I am OK with that, so I will use it here).
One solution is
given...
public class Bar {
private Foo obj;
....
}
we add...
public class Bar {
@Mixin Foo.class
private Foo obj;
....
}
and whala! we are done (would also like the same system to handle methods, to reduce the learning curve).
The only thing is processing that annotation. How should I go about the transformation? If APT is not the tool to use, what is, or (even better) is there an existing mechanism that handles this?
Todd_Musheno@yahoo.com
PS: Open to suggestions, and I am not sure if I am misunderstanding what the code you gave me was to do (I am looking for something I can reproduce locally).
> and whala! we are done (would also like the same
> system to handle methods, to reduce the learning
> curve).
Its not magic.
>
> The only thing is processing that annotation. How
> should I go about the transformation? If APT is not
> the tool to use, what is, or (even better) is there
> an existing mechanism that handles this?
OK, If you really want this, it can be done. I am happy to guide you through the process, but I am not going to do it for you.
If you are up to the challenge, the first step is to put aside the how until we know the what.
To that end you need to decide what you are going to generate.
As has been stated previously, you can generate superclasses, subclasses and helper classes. Because we want to add the methods of Foo to Bar, we discard the helper class strategy as not worthy of further investigation.
For now we will work with a concrete example, and later we will make it work generically.
Exercise 0.
We define what Foo looks like, in such a way that it covers the various possibilities we might get in real life later on.
interface Foo {
void doSomething() throws IOException;
List<Integer> findWhatsits(Collection<Integer>);
}
Your job is to write an implementation of Foo for testing purposes.
doSomething should write "Hello World" to std out.
findWhatsits() should return an empty List.
If you are into TDD, write some unit tests to test Bar.
Exercise 1.
Your job is rewrite Bar so that it has a superclass called Gen, and write Gen so that it achieves everything you want. Our long term plan is to have Gen created automatically, but don't worry too much about that now.
hint, you will either need to use reflection in Gen to access the "obj" field in Bar, or compromise your requirements so that it only works with a getter method for the Foo (and not with fields)
A successful solution must compile and run as expected.
Exercise 2
Your job is to rewrite the orginal Bar so that it has a subclass called Gen, and write Gen so that it achieves everything you want.
hint, if Bar has a factory method rather than a public constructor, you can hide the fact that you are actually using a subclass of Bar.
A successful solution must compile and run as expected.
Exercise 3
Evaluate the results of exercise 1 and exercise 2, and decide which is the most appealing strategy. If you were unable to acheive one of the exercises, then the other is the chosen one. If you were unable to achieve either, try again.
Exercise 4
Currently Bar extends Object implicitly. Rewrite Bar and Gen as though you want Bar to be a subclass of this classclass Base {}
Report back here with the code for Bar and Gen for the chosen strategy.
The way you are explaneing it, will work for the foo bar problem, but ONLY for the foo bar problem!I am looking for a generic solution.Todd_Musheno@yahoo.comPS: Thanks for trying.
Todd,
You want help right?
You can't work this out for yourself, right?
Assuming the answer is yes to both, then I am taking you through the steps I use to solve this sort of problem. We cannot build a generic solution if we do not even know what a single instance of that solution will look like. That is why we are solving it by hand for the FOO BAR problem. If we can't understand how to do it for FOO BAR, then there is no way we are going to be able to do it for the generic case.
This first step (and there are many more to come) takes you through evaluating which of subclass, superclass or helperclass strategies we will employ, and gives us one example that we know will compile. Thats kind of a target we can aim our generic solution at later.
Also it is much easier to fix compiler errors in hand generated code (we do it all the time) than in processor generated code because IDE's don't tell us that our print statements are printing out something with a syntax error, so it becomes an edit compile processor, run processor compile processed code loop to find the bugs. Trust me that this is way simpler if we know exactly what our generated code is supposed to look like. Our initial tests will generate Gen for Bar, so we know exactly where we are heading.
Once you do those exercises, I will take you through more steps, and we will get there eventually if you want to.
both the rapt projects I referred you to are similar to what you require, but also different from what you require, which is somewhat between the two. Your requirement provides an interesting and useful example. It is also I believe about the right size as a training exercise.
By doing it here in the forum, it hopefully provides a resource for others as well.
Are you with me or not?
cheers
Bruce
Exercise 0public final class Foo extends Base implements Bar {
// Note: @delegate tag IS the Bar implementation
@delegate( Bar.class )
public Bar getBar() {
return new Impl();
}
}
public class Impl implements Bar {
public String getString() { return "Hello"; }
}
Exercise 1
public interface Bar {
public String getString();
}
Exercise 2
I do not undertand the task, as Foo is already extending Base (a business class that we HAVE to subclass, implementation unimportant for this discussion).
Exercise 3
I like this solution, but it still will not compile (Duh, thats why I am here).
Exercise 4
Done, see above.
I think I am with you, and that should answer thoes questions...
Now what?
Todd :-)
Hi Todd,
You said earlier
> Lets say we have an interface "Foo", and we are writing a class "Bar".
So I stuck with that.
I cannot understand why in exercise 0, your implementation of Foo (a supplied interface) was a class called Foo that did not implement the interface called Foo.
here is my solution to exercise 0
Exercise 0 Solution
class FooImpl implements Foo {
boolean bang;
public void doSomething() throws IOException {
System.out.println("Hello World);
if(bang) throw new IOException("just testing");
}
public List<Integer> findWhatsits(Collection<Integer> c) {
return Сollections.emptyList();
}
}
Exercise 1 and 2 explore the subclass and superclass strategies.
Exercise 1 - Partial worked solution
Your job is rewrite Bar so that it has a superclass called Gen, ...
class Bar extends Gen {
private Foo obj = new FooImpl();
}
... and write Gen so that it achieves everything you want. Our long term plan is to have Gen created automatically, but don't worry too much about that now.
class Gen implements Foo {
private Foo delegate;
private Foo getDelegate() {
if(delegate == null) {
// "obj" is the name of the Foo field we are delgating to in the subclass
delegate = (Foo)this.getClass().getDeclaredField("obj").get(this);
}
return delegate;
}
public void doSomething() throws IOException {
getDelegate().doSomething();
}
public List<Integer> findWhatsits(Collection<Integer> c) {
return getDelegate().findWhatsit(c);
}
}
You'll need to deal with the exceptions in getDelegate().
You might notice here how the void method and non void method are structurally different inside Gen. That is why I chose those two methods for Foo so that you can see that difference. Long term, our generator for the generic solution will need to take this into account.
With reference to "hint, you will either need to use reflection in Gen to access the "obj" field in Bar, or compromise your requirements so that it only works with a getter method for the Foo (and not with fields)"
can you see how if the actual delegate instance (a Foo) in Bar was accesed via a method, rather than a field, we could make that method abstract in Gen and avoid all the reflection? (but the method in Bar could not be private).
My preference would be to impose this restriction (the delegate is obtained by a method, not a field) and have non reflective code in Gen. What's your preference? If you want to dump the reflection, modify Gen and Bar accordingly.
Back to you...
A. Check that my code compiles, and tweak it if it doesn't.
B. Can you do exercise 2, similar to 1, but have Gen be a subclass of Bar, and Bar will need to use a factory method rather than a constructor (so it can return a Gen).
I'm expecting you to dislike the exercise 2 subclass solution, so once you get to that point, give up. The goal is to realise which strategy (superclass, subclass or helper class) is going to work best. If you prefer the subclass strategy that is fine, we can work with that, but you'll need to write compilable code for Bar and Gen using that strategy.
Exercise 4 is a simple change that is necessary to capture another aspect of a generic solution. Although a trivial code change, it is important to do it because in a subsequent step we are going to work out what information we need in order to generate Gen but which we cannot derive for the plain (unannotated) code of Bar and Foo. We will then define annotations to hold this additional information. The base class of Bar is one of these extra things we need to tell the processor / code generator.
Post back the completed Bar and Gen.
cheers
Bruce
Yet again you are moving the delegation into places it should not go.
The delegation of the interface to the member, should be done in the class in question.
The problem with your solution is the class in question cannot extend other classes.
What I am looking for is a way to have a sub class Foo implement a interface Bar, and do so using its access to Impl (which implements Bar), and be able to use it over and over again for ANY interface I want Foo to implement (assuming we have a referance to an object that implements that interface).
Todd
PS: Let me know if you have questions.
I can see you are missing the point still (man I am bad at talking).
The goal is a mechinisum to handle delegation...
Delegation is java's solution to multiple inheritance (IE: create objects that do what is intended to implement the required interfaceS, and then create an object that delegates to thoes objects for implementation defined in the required interfaces.
Currently there is no easy, clean, quick way to do this (the only way to handle this is through codeing out EVERY dependancy), where you may have 1 interface and 1 object you want to do this delegation for, there may be 1,000+ methods you must
public Object method() { return del.method; }
for. Although this is simple for small interfaces, when you have 4-5 methods, and 20 interfaces, this becomes a maintance nightmare!
I have shown how SYMANTICLY the annotation tool could handle this, but am open to other options.
Todd
> Yet again you are moving the delegation into places
> it should not go.
"Should not" is in terms of your conceptual goal, but where it must go conceptually is practically impossible (unless you use AOP) because APT/JSR269 does not allow modification of existing classes. Therefore the solution involves a transformation of the problem to isolate the generated code in a separate class, much as you transform an algebra problem to get the unknown isolated on one side of the '='.
> The delegation of the interface to the member, should
> be done in the class in question.
but it can't be, but if it is done in the superclass (Gen) , we can inherit that behaviour and it is as good as if it were done in the class in question.
> The problem with your solution is the class in
> question cannot extend other classes.
It can, but not directly. That is the point of exercise 4. "Gen" can extend "Base" which means Bar extends Base as well, Later we need to find a way to tell the processor|generator what Gen's superclass should be. That will require another annotation in addition to the @Delegate annotation.
> What I am looking for is a way to have a sub class
> Foo implement a interface Bar, and do so using its
> access to Impl (which implements Bar), and be able to
> use it over and over again for ANY interface I want
> Foo to implement (assuming we have a referance to an
> object that implements that interface).
yeah, yeah, yeah, I know, except, you got your Foos 'n' Bars inverto, because before you said
"Lets say we have an interface "Foo", and we are writing a class "Bar".
Bar needs to implement Foo, but how!"
> Todd
> PS: Let me know if you have questions.
Yes, here is my question. if Gen extends some arbitrary class Base (which is my solution to exercise 4), can you see how the code I wrote in "Exercise 1 - Partial worked solution" gives you the functionality you require for the single case of delegating to a Foo from Bar?
Is there anyone else who can help with this?
Bruce, thank you for your time, and I realize the annotation solution MAY not be the best, but you make suggestions I dont understand (what is AOP?), and direct me tward your cookie cutter solution to 50% of the problem, and then call yourself done.
Although I appriciate your solution, and think that is usefull in its way, I do not see the same functionality as what I ask for, and in fact, I do not think you understand the problem at hand.
Although I appciate your time greatly, you really have not been helpful.
I know I can do this type of processing with other tools (I have a Doclet that does it for me currently), but think a more standard (compile time) way of doing this would be better all around.
Unless you can ANSWER the questions I asked, or suggest a SOLUTION the provides AT LEAST the same functionality, I think I am not going to get more out of our conversation.
In fact I asked several times if there was a way I can "suggest" this functionality, and you still have not directed me in any way.
Please feel free to contact sun, and have one of there reps/techies review this, as it seems this problem is over your head.
Sorry if this comes out ugly, it is not my intention, but it looks like one my read it that way.
Todd
I think the problem we are having is in terms of the goal.
You are suggesting side files where all of the work is done, this will lead to a difficult to understand class, as one must understand the class in question plus this side file.
What I am proposing is a simple elegant way to allow for the benefits of multiple inheritance through Java.
If you talk to almost any Java designer about multiple inheritance, you will get "delegate and implement the interface" 99.9% of the time.
OK, whats wrong with that?
Simple most of the time you wind up with lots of methods that do not need to be implemented.
Plus...
Lets say
interface A { public void method1(); }
interface B { public void method2(); }
and we follow this pattern...
If we add void method2() to interfaceA, why should we use ImplementationB, instead of ImplementationA? Souldent we get a compile time error as we are not implementing InterfaceA.method2()?
This is a HUGE problem in design, and the .NET people are laughing at us, and I am getting sick of it.
Let me know if anyone has a suggestion to this COMMON problem.
Todd
i hope the exercise is seen through to completion...i have no specific use for it but i am interested in annotations as design patterns
any other well-known patterns as annotations out there?
would love to see them as other threads
do not know if it qualifies as a design pattern but i posted re to Resource Injection
...making Java declarative one pattern at a time
I don't think this qualifies as a design pattern, rather a solution to the Multiple Inheritance issue. And I am open to other alternatives, so I don't care if this ever gets into the API, as long as some (ANY) solution to the problem is found.
Design wise the current, code "millions of delegation methods" is fine, but the problem is the compiler will not find faults when I think it should (IE: inheritance collision, for lack of a better term).
So basically we are currently saying, when we change a designs inheritance model, we can rely on developers to catch this issue 100% of the time, and I do not think this is acceptable (If i am the minority, never mind, but I doubt that I am).
Todd
PS: This would be a methodology issue, not a design one.
if i understand correctly this sounds like the Adapter design pattern
http://www.patterndepot.com/put/8/adapter.pdf
what about this as an alternative syntax...
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface Delegate {
Class<?>[] value();
}
public abstract class AbstractFoo
implements Comparable, Closeable, Readable, Runnable {
// Closeable and Readable implementor
@Delegate( {Closeable.class, Readable.class} )
protected StringReader sr = new StringReader("hello.world");
// Comparable implementor
@Delegate( Comparable.class )
protected Integer i = new Integer(1);
// Runnable implementor
@Delegate( Runnable.class )
protected Thread t = new Thread();
...
// creator method returning apt-generated class
public static AbstractFoo create() {
return new ConcreteFoo();
}
protected AbstractFoo() {}
}
apt would generate an extension of AbstractFoo, say ConcreteFoo, that delegates the appropriate method(s) to the annotated field instances, compiler-checking everything necessary
note: for this to work the fields should be protected allowing ConcreteFoo the access it would need to AbstractFoo
note: no public construction, method provided to return an expected delegated/multi-inherited/adapted concrete instance
added comments
Message was edited by:
developer_jbs
This is similar to where I was heading.
But this uses the generated subclass mechanism, which I believe is a little more awkward than the generated superclass mechanism.
The generated subclass mechanism is awkward because
+ without adding another annotation, the only way for javac (or apt) to work out the name of the class to generate is to look inside the create method (and they can't at least in theory and in a standardised way) whereas they can determine the name of the supeclass if that is to be generated.
+ If you solved that problem with another annotation specifying the generated subclass, then there is no way to enforce that the create method actually returns an instance of that generated class - which means if someone goofs up, we don't know till run time - I prefer compile time problem detection if possible.
+ The factory method seems like something that should be generated, but has to be coded by hand to make the other generated stuff work.
+ you can't write subclasses of AbstractFoo, it is effectively final. Sometimes you might want to use the @Delegate in a superclass.
A generated subclass has an advantage (if you look at it with certain coloured spectacles) of allowing the delegatees to be fields, which a generated superclass mechanism could not get to unless it used reflection. For a non reflective generated superclass, you need to declare accessor methods so that the superclass can get the delegatees (it would declare them as abstract methods). But then again, reflection is no big deal these days, and if its in generated code, you don't even have to write it, or even know that it is there.
The problem with developer_jbs's suggestion is as follows...
public final class AbstractFoo
implements Comparable, Closeable, Readable, Runnable {
//etc...
}
This will not work, as the class in question is needs to be to be final, due to the design.
Thanks
Todd
The problem with brucechapman's post.
IF I am understanding this properly, it will still not allow for final classes, or a pantheon of constructors.
Here is the issue again...
given:
public interface SomeInterface1 {
public void method1();
}
public interface SomeInterface2 {
public void method2();
}
public final class Foo implements SomeInterface1, SomeInterface2 {
public Foo( ... ) { ... }
private SomeInterface2 getSomeInterface1() {
return ...;
}
private SomeInterface2 getSomeInterface2() {
return ...;
}
}
We would do this...
public final class Foo implements SomeInterface1, SomeInterface2 {
public Foo( ... ) { ... }
private SomeInterface2 getSomeInterface1() {
return ...;
}
private SomeInterface2 getSomeInterface2() {
return ...;
}
public void method1() {
getSomeInterface1().method1();
}
public void method2() {
getSomeInterface2().method2();
}
}
So then, years later we make a change to SomeInterface1...
public interface SomeInterface1 {
public void method1();
public void method2();
}
At this point Foo has the implementation for SomeInterface1.method2() as follows...
public void method2() {
getSomeInterface2().method2();
}
but that may be wrong!
public void method2() {
getSomeInterface1().method2();
getSomeInterface2().method2();
}
Maybe correct, or...
public void method2() {
getSomeInterface1().method2();
}
Might be whats intended at this point.
The honorable thing is that the compiler did not say a word that we did not implement that method for getSomeInterface1.method2()
This would be fine if we were actually developing an implementation, but as we are simply delegating to another object, I believe this should be caught by the compiler.
The question is, how do I fix this problem.
Todd
> The problem with developer_jbs's suggestion is as
> follows...
> public final class AbstractFoo
> implements Comparable, Closeable, Readable, Runnable
> {
>//etc...
> This will not work, as the class in question is needs
> to be to be final, due to the design.
final? huh? i wrote abstract
> + without adding another annotation, the only way for
> javac (or apt) to work out the name of the class to
> generate is to look inside the create method (and
> they can't at least in theory and in a standardised
> way) whereas they can determine the name of the
> supeclass if that is to be generated.
how about
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE}) // for class decl
public @interface Adapter {
String adapterName default "";
}
@Adapter( "ConcreteFoo" )
public abstract class AbstractFoo
implements Comparable, Closeable, Readable, Runnable {
...
// at least protected to allow generated subclass to access
protected /*or public*/ AbstractFoo() {}
}
> + If you solved that problem with another annotation
> specifying the generated subclass, then there is no
> way to enforce that the create method actually
> returns an instance of that generated class - which
> means if someone goofs up, we don't know till run
> time - I prefer compile time problem detection if
> possible.
lose the create method and just use adapterName, ConcreteFoo
if misused this would be caught at compile-time (during apt rounds)
> + The factory method seems like something that should
> be generated, but has to be coded by hand to make the
> other generated stuff work.
no need to have it now, use ConcreteFoo constructor, it will be generated and compile-time checked for usage
> + you can't write subclasses of AbstractFoo, it is
> effectively final. Sometimes you might want to use
> the @Delegate in a superclass.
i don't follow, being abstract, it is primed for extension
what do you see?
maybe Delegate and Adapter annotation would benefit from @Inherited meta-annotation, less declarative but more reuse
>> + you can't write subclasses of AbstractFoo, it is
>> effectively final. Sometimes you might want to use
>> the @Delegate in a superclass.
> i don't follow, being abstract, it is primed for extension
what do you see?
Because part of the implementation is in a SPECIFIC subclass (which happens to be generated), then if we create a different subclass, we lose that part of the implementation. What needs to be done is subclassing the generated class, but this cannot be enforced.
Sure it is possible to get it right, just as your other suggestion of using the generated constructor is possible. What I am saying is that in my experience generating a superclass seems to cause a simpler solution.
One reason for saying this is that the generated class is much less visible in the hand written code, and it is harder to accidentally do wrong things in code that is using the FooPair (the FooPair being the pair of hand written and generated classes that work together to achieve the whole, Foo being the hand coded part).
Here is a quick list of possible abuses of a generated subclass
* construct an instance of the superclass instead of the subclass
* make another subclass of the superclass instead of subclassing the generated class.
In a general sense the generated class is part of the implementation of the class, rather than part of its spec. The rules of encapsulation should be a target in how we design the annotation processor and the generated class. Generating a subclass exposes that implementation detail as a big part of the contract. (subclass Gen not Foo, construct Gen not Foo). IMHO generating a superclass does a much better job of encapsulating that implementation detail. All the details about using the generated class are inside the class that causes it to be generated (Foo), rather than in the classes that use Foo. This is a good thing.
Another thing that is difficult with a generated subclass is to override part of the generated code.
For example, to answer Todd's question about what to do when two delegatees have the same method, we could say the annotation processor just generates an error. However we can do better than that, such as
* If all but one of the delegatees having the method, have it as not part of the interface being implemented by each delegatees (ie only one has the method in its specified interface), then delegate to the one which uses it to implement the specified interface.
* Otherwise (more than one of the interfaces has that method signature), then the hand written class must manually delegate in whatever manner is desired.
So implementing those rules is different depending on whether you generate the superclass or the subclass. If you generate the superclass, you generate an abstract method, forcing the hand coded method to implement it. For a generated subclass, the annotation processsor would need to check that the method existed in the hand coded class (and cause an error if it didn't), and not generate that method at all.
If we wanted to allow the coder to override any of the generated methods with some special implementation, then that is trivial with a generated superclass (provided the generated methods are not "final").
However for a generated subclass it gets a little messy. The coder cannot use an @Override annotation on the method because it doesn't override. The generator can look for methods in Foo that have the same signature as a method about to be generated, and skip the generation of that method if it is Overridden (underridden?), but by doing this we lose the ability for the compiler/annotation processor to tell us when we have got method signatures wrong. Also if we want to call the generated method from within the "overriding" method, we are out of luck because it doesn't (can't) exist in the subclass, so we have to hand code the full implementation of that method.
For all these reasons I'd generate the superclass.
Message was edited by:
brucechapman
> Because part of the implementation is in a SPECIFIC
> subclass (which happens to be generated), then if we
> create a different subclass, we lose that part of the
> implementation. What needs to be done is subclassing
> the generated class, but this cannot be
> enforced.
i guess i am confused
to be clear, i am trying to provide a declarative solution for multiple inheritance in Java
i am not recommending this approach to solve any other software design issue or be applied to any object model
annotations and annotation processing via apt are my chosen implementation
> Sure it is possible to get it right, just as your
> other suggestion of using the generated constructor
> is possible. What I am saying is that in my
> experience generating a superclass seems to cause a
> simpler solution.
for my purpose, getting it, multiple inheritance, right is compiler-enforced
superclass as simpler solution for what?
> One reason for saying this is that the generated
> class is much less visible in the hand written code,
> and it is harder to accidentally do wrong things in
> code that is using the FooPair (the FooPair being the
> pair of hand written and generated classes that work
> together to achieve the whole, Foo being the hand
> coded part).
i am not sure what wrong things can be done to my solution,
the intent, having achieved multiple inheritance, a developer is free to use or extend the abstract or concrete, super or sub, classes as he sees fit
> Here is a quick list of possible abuses of a
> generated subclass
>
> * construct an instance of the superclass instead of
> the subclass
superclass is abstract, cannot instantiate
> * make another subclass of the superclass instead of
> subclassing the generated class.
don't see a problem with this
> In a general sense the generated class is part of the
> implementation of the class, rather than part of its
> spec.
sounds right, the generated, concrete class is the implementation of the abstract one
>The rules of encapsulation should be a target
> in how we design the annotation processor and the
> generated class.
i do not understand, and i am always skeptical about the word "should"
>Generating a subclass exposes that
> implementation detail as a big part of the contract.
> (subclass Gen not Foo, construct Gen not Foo). IMHO
> generating a superclass does a much better job of
> encapsulating that implementation detail. All the
> details about using the generated class are inside
> the class that causes it to be generated (Foo),
> rather than in the classes that use Foo. This is a
> good thing.
i really don't get it
> Another thing that is difficult with a generated
> subclass is to override part of the generated code.
>
> For example, to answer Todd's question about what to
> do when two delegatees have the same method, we could
> say the annotation processor just generates an error.
why is this an error condition? in my model, if you annotate two fields with the same delegate type, say class Bar has @Delegate( Runnable.class ) annotated on fields Thread t1 and Thread t2, that seems to suggest that for the intended implementation of Bar.run() should delegate to t1.run() and t2.run() (bad example but you get the point)
> However we can do better than that, such as
>
> * If all but one of the delegatees having the method,
> have it as not part of the interface being
> implemented by each delegatees (ie only one has the
> method in its specified interface), then delegate to
> the one which uses it to implement the specified
> interface.
the delegatees are generated in my approach, the methods to delegate are specified by the interface type in the annotation, @Delegate( Interface.class )
> * Otherwise (more than one of the interfaces has that
> method signature), then the hand written class must
> manually delegate in whatever manner is desired.
my delegates are generated
> So implementing those rules is different depending on
> whether you generate the superclass or the subclass.
> If you generate the superclass, you generate an
> abstract method, forcing the hand coded method to
> implement it. For a generated subclass, the
> annotation processsor would need to check that the
> method existed in the hand coded class (and cause an
> error if it didn't), and not generate that method at
> all.
can you provide an example?
> If we wanted to allow the coder to override any of
> the generated methods with some special
> implementation, then that is trivial with a generated
> superclass (provided the generated methods are not
> "final").
coder is free to override generated methods, subclassing is also trivial in my approach
> However for a generated subclass it gets a little
> messy. The coder cannot use an @Override annotation
> on the method because it doesn't override. The
> generator can look for methods in Foo that
> have the same signature as a method about to be
> generated, and skip the generation of that method if
> it is Overridden (underridden?), but by doing this we
> lose the ability for the compiler/annotation
> processor to tell us when we have got method
> signatures wrong. Also if we want to call the
> generated method from within the "overriding" method,
> we are out of luck because it doesn't (can't) exist
> in the subclass, so we have to hand code the full
> implementation of that method.
again, not following, example?
> ....example ....
fair enough.
I'll rework your code to show how it might look if the superclass were generated. (I'll drop the runnable bit because it doesn't add anything not already covered by the other examples).
Sometimes I say generate the superclass. What I mean here is that the generated class is the superclass of the class that causes it to be generated, compared with your proposal to generate a subclass. I am trying to convince you that generating the superclass is at least another design option for you, and I believe it is the better option, if having considered that you disagree, thats fine, but I at least want you to consider it.
public class Foo extends FooGen {
// Closeable and Readable implementor
@Delegate( {Closeable.class, Readable.class} )
protected StringReader sr = new StringReader("hello.world");
// Comparable implementor
@Delegate( Comparable.class )
protected Integer i = new Integer(1);
}
The annotation processor for @Delegate looks at the declared supertype of Foo and generates that class. You don't need another annotation to specify the name of the generated class.
FooGen would look something like this
abstract class FooGen implements Closable, Readable, Comparable {
private StringReader getSr() {
this.getClass().getDeclaredField("sr").get(this): // details ommitted
}
//... similar for geti(){ ...}
public void close() {
getSr().close();
}
// etc delegate to other methods
}
Can you see how the details of FooGen are not visible to a user of Foo (unless it is in the same package)?
Foo can be subclassed directly without breaking Foo.
The generated delegate methods can easily be overridden in Foo if that is desired.
Foo can have whatever constructors it likes. FooGen doesn't care.
So now I'll address your questions
> to be clear, i am trying to provide a declarative
> solution for multiple inheritance in Java
> i am not recommending this approach to solve any
> other software design issue or be applied to any
> object model
> annotations and annotation processing via apt are my
> chosen implementation
understood
> for my purpose, getting it, multiple inheritance,
> right is compiler-enforced
> superclass as simpler solution for what?
generating the superclass rather than the subclass (a design choice when designing a mechanism that uses annotation/annotation processor/generated classes)
>
> i am not sure what wrong things can be done to my
> solution,
> the intent, having achieved multiple inheritance, a
> developer is free to use or extend the abstract or
> concrete, super or sub, classes as he sees fit
Yes but some things can be worth precluding when designing something.
Generating a subclass precludes overriding a generated method inside the hand written class. That is not something I'd choose to preclude.
Generating a superclass precludes subclassing the hand written code without subclassing the generated code. That is something that is worth precluding.
>
> > Here is a quick list of possible abuses of a
> > generated subclass
> >
> > * construct an instance of the superclass instead
> of
> > the subclass
>
> superclass is abstract, cannot instantiate
Sure if the annotation processor enforced that, but it is something else the author must remember to do in order to make the @Delegate annotations work.
> sounds right, the generated, concrete class is the
> implementation of the abstract one
but generating a super abstract class is another design option, which I think gives a better design.
>
> >The rules of encapsulation should be a target
> > in how we design the annotation processor and the
> > generated class.
>
> i do not understand, and i am always skeptical about
> the word "should"
The generated class is part of the implementation of Foo (say), encapsulation says that implementation detail shouldn't leak out into Foo's specification. Thta allows us to change how we implement Foo, without breaking anything that uses Foo. This is a good principle to follow even for generated classes, if we can.
Generating a superclass does a better job of encapsulating the generated class than generating a subclass.
> > Another thing that is difficult with a generated
> > subclass is to override part of the generated code.
>
> >
> > For example, to answer Todd's question about what
> to
> > do when two delegatees have the same method, we
> could
> > say the annotation processor just generates an
> error.
>
> why is this an error condition? in my model, if you
> annotate two fields with the same delegate type, say
> class Bar has @Delegate( Runnable.class ) annotated
> on fields Thread t1 and Thread t2, that seems to
> suggest that for the intended implementation of
> Bar.run() should delegate to t1.run() and t2.run()
> (bad example but you get the point)
If you are going to write a code generator you need to deal with these cases. For your example of run() what determines the order?
For say a duplicate comparable what do you do? running two compare functions one after the other does not give you a combined result. You need an explicit mechanism. Todd and I are suggesting the a trivial solution is to make it illegal, and I am suggesting that a better mechanism is to force the code author to specify what to do. in other words to manually resolve the ambiguity. In the generated superclass mechanism this is easily done by generating a single abstract method forcing the coder to resolve the ambiguity in a way appropriate to what they are trying to achieve. It is not impossible with a generated subclass, but it is not as elegant.
The method conflict can also occur with one instance of each interface if two interfaces have a common method signature.
>
> coder is free to override generated methods,
> subclassing is also trivial in my approach
>
But they have to override them in a new subclass of the generated class, and it is that subclass that the coder must then use. It is all starting to get a bit ugly, not too ugly but a little ugly. That ugliness pretty much disappears if you generate a superclass rather than a subclass.
Sorry I should have given an example sooner.
Can you see what I am on about now?
Cheers
Bruce
developer_jbs:The problem with your solution is that it does not work, as it will not work with final classes. Please explane how you can get APT to handle this if I am mistaken.Sorry for the confusion...Todd
In response to brucechapman Re: RFE: Delegate Annotation Jun 6, 2007 2:17 AM (reply 24 of 24) ...
I like where you are going with, this but this precludes you from extending anything, how do you solve for it if the requirement for Foo was to additionally extend Bar (some other class, not an interface)?
Or do you think this is an issue (I think it is a big one)?
Todd
> The problem with your solution is that it does not
> work, as it will not work with final classes.
there are no final classes in any of my examples
i am proposing a hand-written <i>abstract</i> superclass (abstract because it claims to implement interface(s) but purposely doesn't, allowing apt to generate a subclass with the delegation pattern
please, see my posted snippets
> Please explane how you can get APT to handle this if I am
> mistaken.
apt is used to generate a concrete subclass
bruce commented that in my approach one might as well declare one of the classes final, but i rebutted...(discussion. discussion)...and that brings us here, where i am considering bruce's approach (and liking it)
bruce, i'll have questions later
> In response to brucechapman Re: RFE: Delegate
> Annotation Jun 6, 2007 2:17 AM (reply 24 of 24) ...
> I like where you are going with, this but this
> precludes you from extending anything, how do you
> solve for it if the requirement for Foo was to
> additionally extend Bar (some other class, not an
> interface)?
> Or do you think this is an issue (I think it is a big
> one)?
> Todd
Todd, I'm glad you asked.
Actually, I need to confess that I was holding out on you guys. Of course this is important, but I didn't want to add that level of complexity until you were at least starting to understand the idea of generating a superclass.
So now I can reveal a cople of ways of dealing with this situation.
The generated class can extend whatever class the author of Foo wants to extend. The issue is how do you tell the annotation processor what that class is, so that when it writes the source code, it can write the extends clause.
A seemingly obvious solution is to add another annotation (or in other situations where the code generation is triggered from a Target=TYPE annotation, simply another element on that annotation). However bearing in mind that it is not in fact a class which you extend but a type, specifically you might want to extend a generic type (such as ThreadLocal<Integer>). At this point using an annotation gets rather ugly because we cannot specify types intuitively using annotations.
Therefore the following is what I have used in the past.
We are already using Foo's extends clause to specify the name of the generated class, so by adding a type parameter to the superclass, you can pass any type through to the annotation processor, which it can use as the extends clause of the generated class, AND it can can make sure the generated class has a matching type variable so everything compiles.
In the example I showed in reply 24 above, say we wanted Foo to extend ThreadLocal<Integer>
public class Foo extends FooGen<ThreadLocal<Integer>> {
// Closeable and Readable implementor
@Delegate( {Closeable.class, Readable.class} )
protected StringReader sr = new StringReader("hello.world");
// Comparable implementor
@Delegate( Comparable.class )
protected Integer i = new Integer(1);
/* our ThreadLocal<Integer> implementation */
private int next=0;
protected Integer initialValue() { return next++; }
}
The annotation processor not only gets the name of the generated class, from the extends clause, but also get the generated class's superclass from the extends clause's type parameter, and generates this
abstract class FooGen<T> extends ThreadLocal<Integer> implements Closable, Readable, Comparable {
private StringReader getSr() {
this.getClass().getDeclaredField("sr").get(this): // details ommitted
}
//... similar for geti(){ ...}
public void close() {
getSr().close();
}
// etc delegate to other methods
}
wow...(pause)...wowwhat a solutioni took a look at rapt and must say that is real innovation
I agree, this is great!!!
The only problem I have currently is how to "Hide" FooGen from the rest of the world, and how to allow it to have what ever name we want. I would love to make it (borrowing from C) a Friend to Foo, and nothing else, but I think this solution works, especially if we make it a package level class (maybe that should be a parameter too? might we need that public?).
Also I wish there were a way to hide that little daemon from the mix, looking at Foo, it is a bit confusing that it extends FooGen, and not the intended type, but I am ok with it as is, though I say there is room for improvement there.
The other issues are quasi small...
Getting the compiler to die on collision.
Generating the proper superclass for the type.
Parsing and generating the proper method details for the super.
So, knowing we have done no development, are we ready to make an actual proposal for inclusion to the JDK? or what are our next steps? or whats wrong with what we have?
Todd
> The other issues are quasi small...
>Getting the compiler to die on collision.
if you can detect it here is how you can stop compilation
/** Put out a compiler error, halting compilation */
private static void fatal(
final AnnotationProcessorEnvironment theEnv,
SourcePosition sp,
String msg ) {
if( sp == null ) { theEnv.getMessager().printError( msg ); }
else { theEnv.getMessager().printError( sp, msg ); }
}
getMessager().printError( sp, msg ); }
> o, knowing we have done no development, are we ready
> to make an actual proposal for inclusion to the
> JDK? or what are our next steps? or whats wrong
> with what we have?
i believe bruce has been doing development...working on post-Mustang jsr-269, right?
bruce, might we see the rapt ideas in it? is rapt the incubator for 269?
i've been talking to the jsr-273 folks (also scheduled for a post-Mustang release) about some of my annotations but my apt additions do not seem t o be a good match for 273's intent
bruce, would you mind commenting on my recent threads in this forum? please
Message was edited by:
developer_jbs
added request
> I agree, this is great!!!
> The only problem I have currently is how to "Hide"
> FooGen from the rest of the world, and how to allow
> it to have what ever name we want. I would love to
> make it (borrowing from C) a Friend to Foo, and
> nothing else, but I think this solution works,
> especially if we make it a package level class (maybe
> that should be a parameter too? might we need that
> public?).
Making it package level is the best that can be done AKAIK. Sure friend would be nice, but we don't have that facility in the language.
Doing stuff with annotations that would otherwise require a language enhancement is always an exercise in balancing ugliness with functionality. Sometimes despite our best efforts the solution is uglier than it is beneficial, in these cases euthanasia of the idea is the best course, freeing us to expend effort on those ideas where we can keep the ugliness in check.
> Also I wish there were a way to hide that little
> daemon from the mix, looking at Foo, it is a bit
> confusing that it extends FooGen, and not the
> intended type, but I am ok with it as is, though I
> say there is room for improvement there.
I agree on the desire, but on current technology, I can't see any way to improve it. If you can, let's hear it.
> The other issues are quasi small...
>Getting the compiler to die on collision.
No problem, and Jamie has already replied on that, tho' I haven't checked his reply for accuracy yet. However I think forcing the user to resolve the ambiguity is a better solution to ambiguity. As already stated, the generated class would declare any ambiguous methods as abstract. If the user has not foreseen the ambiguity, the compiler will eventually die because that abstract method will not be overridden. The user then has the option of either removing the ambiguity by changing the @Delegate annotations to avoid the ambiguity, or by providing an implementation of the abstract method which handles the ambiguity in an appropriate manner to the circumstances.
> Generating the proper superclass for the type.
I don't understand what you mean, unless you mean what does the annotation processor look like that generates the superclass.
> Parsing and generating the proper method details
> for the super.
Just grunt in the annotation processor.
> o, knowing we have done no development, are we ready
> to make an actual proposal for inclusion to the
> JDK? or what are our next steps? or whats wrong
> with what we have?
Slow down a bit.
If you really want this in the JDK, the best route AFAIK is to develop it open source, show the benefits, and discover the pitfalls. If it becomes a base technology for other projects with wide adoption, or if parts of the JDK need it, then at that point it could be considered for inclusion. Even if we had a current implementation (and we don't), we'd still be a long way from that point.
> Todd
> > The other issues are quasi small...
> >Getting the compiler to die on collision.
>
> if you can detect it here is how you can stop
> compilation
> [code]
> /** Put out a compiler error, halting
> compilation */
>private static void fatal(
>final AnnotationProcessorEnvironment theEnv,
>SourcePosition sp,
>String msg ) {
> if( sp == null ) {
> theEnv.getMessager().printError( msg ); }
> else { theEnv.getMessager().printError( sp,
> msg ); }
>}
> e]
> getMessager().printError( sp, msg ); }
>
That is using apt. apt was a (hurried) addition to JDK5 to enable annotation processing. JSR269 developed the "standard" way to process annotations at build time. JSR269 defined the javax.lang.model.* packages and javax.annotation.processing package in JDK6.
That is the mechanism that should be used for any new annotation processing. "apt" was cool, it taught us what we could do. It taught us what we needed, it taught us how not to do it. Someone once said something like "If you want good software, do it twice and throw the first one way". apt was the first one (my opinion). the JSR269 and javax packages mentioned above are the second.
Generating errors in the compiler is just as simple using JSR269.
> > o, knowing we have done no development, are we
> ready
> > to make an actual proposal for inclusion to the
> > JDK? or what are our next steps? or whats
> wrong
> > with what we have?
>
> i believe bruce has been doing development...working
> on post-Mustang jsr-269, right?
yes. See above, post-mustang no, JSR-269 reference implementation is in JDK6, above packages and built into javac.
> bruce, might we see the rapt ideas in it? is rapt
> the incubator for 269?
No.
JSR269 defined the API to use to write annotation processors. rapt project explores the edges of what can be done with annotation processing which would otherwise require language changes. My experience of building rapt contributed to the development of the JSR269 APIs. I would like to convert the existing rapt packages over to use JSR269 and I have a slightly better solution than SUX<T1..Tn> to that particular problem.
rapt would be an appropriate home to develop the @Delegate concept.
>
> i've been talking to the jsr-273 folks (also
> scheduled for a post-Mustang release) about some of
> my annotations but my apt additions do not seem t o
> be a good match for 273's intent
>
> bruce, would you mind commenting on my recent threads
> in this forum? please
Time is a precious resource, I'll have a look and respond where I think I can add value.
>
> Message was edited by:
> developer_jbs
> quest
As to bruces last post...What?I don't understand what the meaning of all the numbers are, I am an old Java Developer, and know nothing of the secret society behind sun?Just curiousTodd
no secret society, an open community http://jcp.org/en/jsr/detail?id=269 http://jcp.org/en/jsr/detail?id=273but i am sure the sun programming elite have their own circle ;)
> no secret society, an open community
>
> http://jcp.org/en/jsr/detail?id=269
> http://jcp.org/en/jsr/detail?id=273
>
> but i am sure the sun programming elite have their
> own circle ;)
Yes, few are judged worthy of advancing to the Perihelion of Mercury; oh wait -- I shouldn't have mentioned that!
>
> Yes, few are judged worthy of advancing to the
> Perihelion of Mercury; oh wait -- I shouldn't have
> mentioned that!
Sounds a tad hot to me. I'll keep orbiting a bit further out from the sun, just close enough to bathe in the warmth, not so close as to get burnt.
> >
> > Yes, few are judged worthy of advancing to the
> > Perihelion of Mercury; oh wait -- I shouldn't have
> > mentioned that!
>
> Sounds a tad hot to me. I'll keep orbiting a bit
> further out from the sun, just close enough to bathe
> in the warmth, not so close as to get burnt.
(Don't let anyone know I told you, but reaching the prerequisite level of the Orbit of Venus requires mastery of heat shielding.)
maybe some hip waders...it's getting pretty deep
Ok, enough of this silliness, I like it, but its not productive.I will start work on the annotation and APT processor.It is a shame we cant handle sub-classing as well as delegation (let me know if there are alternatives that handle this, I am not married to APT).Todd
just keeping it lighti find these forums as entertaining as they are helpful
> Ok, enough of this silliness, I like it, but its not
> productive.
> I will start work on the annotation and APT
> processor.
> It is a shame we cant handle sub-classing as well as
> delegation (let me know if there are alternatives
> that handle this, I am not married to APT).
> Todd
1. Please, please, don't use APT.
please use the javax.annotation.processing and javax.lang.model.* packages in Java 6.
For a start it is standardised, and it means the annotation processors will be run when compiling with the eclipse (no that's not a reference to the previous silliness) compiler as well as with javac command line or any of the IDEs that use the javac compiler.
2. There are still a couple of design decisions (quite closely related to each other) that I'd like to gently coax you toward which I think will improve the user interface. (The user interface in this case being what the user's code looks like in order to use the feature) Its a balance of functionality and readability, and trying to achieve the nervana of simpe things being simple, and more complex things being possible. I'll start that in another post.
I find getting the UI as best you can is quite critical for this type of project. Doing this sort of "almost" language extension with annotations, annotation processors and generated code can at best look a bit strange in the code. At worst it can be be so ugly that it is not going to get used. If you put lots of effort into the UI, then some possible projects (but not all), can achieve a state where their functionality outweighs their ugliness. If you don't try really hard on the UI, it is unlikely you will get something that you (let alone anyone else) would want to use. I suspect that the Delegates mechanism can be done quite nicely, or could be done attrociously, my intention is to not let it be the latter.
3. If you want to develop this in the rapt project, that would be fine by me. I'll need to set you up as a committer, and create a new package for the delegates, and probably a new source tree for the JDK 6 stuff to keep it separate from the apt based code that is there already. Do you have a java.net login? Jamie are you in too?
Bruce
> just keeping it lightHA HA HA, good pun.Oh shut up Bruce> i find these forums as entertaining as they are> helpful
In the example we have been using in recent posts we delegate to a Comparable
However Comparable is a raw type, and raw types are only in the language to support backward compatibility.
In our example, we should be delegating to Comparable<Integer>
so the design needs to cover that case.
How are we going to tell the processor to generate a delegate to a generic interface?
(hint less is more)
> Do you have a java.net login?> Jamie are you in too?sure, i'm inmy java.net login is developer_jbs
My java.net id is "musheno" and I would like it clear the problem we are trying to solve...
The automation of delegating to A) local variables or B) results of no arg methods.
"A" is an optional one, and we would like this to eventually migrate to the JDK, so we MUST keep our code clean, simple, easy to understand, easy to verify, and easy to use.
I propose we wait until the majority of us has no problems with it before release.
So...
Lets roll up those selves!
Todd
PS: From my understanding this is a group effort, so group, please show at least a little effort.
> Lets roll up those selves!
and those sleeves :)
Some stuff I need to know to get rapt ready for the invasion.
CVS or subversion?
rapt is currently CVS but there is an option to convert to SVN, which seems like a good idea to me, and now seems like a good time.
In terms of plan, lets get the "design" part finished a bit more.
Then I think it best to develop in two stages.
First lets set a target of having the annotation processor detect all the things that can be wrong, ie people using the Annotation incorrectly, eg no extends clause on the annotated class, we need to do this anyway and annotations processors tend to follow a two stage algorithm,
+ stage 1, find all occurences of the annotation, check usage, and collect and collate data required for stage 2.
+ stage 2, if no errors, generate code.
For stage 1, we need a collection of pieces of code that are misusing the @delegate annotation.
For that we need the rules on what is correct usage. (We we can have one or more test cases for a breach of each rule).
Options for the tests are automate (junit or testng?) or just have a package of stuff that should work, and a package of code that should all generate compiler errors.
Writing unit tests which drive the compiler and feed it small blocks of source code, and make assertions about the errors can be challenging, but isn't too horid once you know how.
current questions:
svn? or CVS?
junit? testng? manual?
previous questions:
How to delegate to a generic type such as Comparable<Integer> ?
Bruce
> CVS or subversion?
i am familiar with cvs, not subversion
in the name of productivity, i vote cvs
> In terms of plan, lets get the "design" part finished
> a bit more.
>
> Then I think it best to develop in two stages.
>
> First lets set a target of having the annotation
> processor detect all the things that can be wrong, ie
> people using the Annotation incorrectly, eg no
> extends clause on the annotated class, we need to do
> this anyway and annotations processors tend to follow
> a two stage algorithm,
>
> + stage 1, find all occurences of the annotation,
> check usage, and collect and collate data required
> for stage 2.
i volunteer for this stage
http://forum.java.sun.com/thread.jspa?threadID=5180814&tstart=0
> For stage 1, we need a collection of pieces of code
> that are misusing the @delegate annotation.
>
> For that we need the rules on what is correct usage.
> (We we can have one or more test cases for a breach
> of each rule).
for direct correspondence my email is developer_jbs@yahoo.com
> Options for the tests are automate (junit or testng?)
> or just have a package of stuff that should work,
> and a package of code that should all generate
> compiler errors.
familiar with junit, vote for manual
> How to delegate to a generic type such as
> Comparable<Integer> ?
will all/some/no correspondence be via this forum?
Re: RFE: Delegate Annotation
Jun 13, 2007 6:56 PM (reply 48 of 48)
Click to email this message
> CVS or subversion?
i am familiar with cvs, not subversion
in the name of productivity, i vote cvs
>>> I used to be a CVS guy until I used svn, and noticed no difference, I vote SVN.
> In terms of plan, lets get the "design" part finished
> a bit more.
>
> Then I think it best to develop in two stages.
>
> First lets set a target of having the annotation
> processor detect all the things that can be wrong, ie
> people using the Annotation incorrectly, eg no
> extends clause on the annotated class, we need to do
> this anyway and annotations processors tend to follow
> a two stage algorithm,
>>> I think we need to get the annotation together first, lets clearly define what needs done/ how many annotations we will need first.
>
> + stage 1, find all occurences of the annotation,
> check usage, and collect and collate data required
> for stage 2.
i volunteer for this stage
http://forum.java.sun.com/thread.jspa?threadID=5180814&tstart=0
> For stage 1, we need a collection of pieces of code
> that are misusing the @delegate annotation.
>
> For that we need the rules on what is correct usage.
> (We we can have one or more test cases for a breach
> of each rule).
for direct correspondence my email is developer_jbs@yahoo.com
> Options for the tests are automate (junit or testng?)
> or just have a package of stuff that should work,
> and a package of code that should all generate
> compiler errors.
familiar with junit, vote for manual
>>> I vote for junit, actualy no, I think we should REQUIRE vigorous unit testing from the begining, it produces better code.
> How to delegate to a generic type such as
> Comparable<Integer> ?
will all/some/no correspondence be via this forum?
I think this forum sould be our "Fallbak" position...
Any other Ideas? I use Yahoo's IM for instant mesaging....
todd_musheno@yahoo.com
Thanks guys.
Jamie, I think you'd like subversion, is there anything to stop you changing / learning it?
On the other hand, the link I had to the instructions on how to convert a project from cvs to subversion is now broken, so converting might not be an option. Will let you know.
I did some work on confirming a few things yesterday. I've got a reasonably easy to use framework for testing compiler / annotation stuff, so although I'm more on the pragmatic rather than zealot range of the scale regarding testing, I think automated tests are a good idea here.
However I came across a problem.
if our code isclass MyClass extends Gen<MySuperClass> {}
then the annotation processor cannot see the name of the superclass nor its type elements. I think this is a bug, because
http://java.sun.com/javase/6/docs/api/index.html?javax/lang/model/type/ErrorType.html
has a getTypeArguments() method, that ought to work in this case. Certainly that was my intent when we worked on that API.
I have patched the compiler to get this working, (small patch 5 lines added, 1 deleted - (@ LOC/hr == 1) so we may need to proceed with a patched compiler until I can get the bug submitted, accepted, and rolled out. We have almost definitely missed the boat for JDK6 U2. I Still need to test my patch against compiler TCK.
Regarding comms channels, rapt has a mailing list and a wiki which can both be used, but we need to consider that there may be other people actively following this thread here, and we need to consider them as well.
So all you silent observers, would you object if we took this discussion elsewhere and posted a link to it in this forum? and as my daughter would say if I spent more than 2 seconds pondering a response THAT"S NOT A RHETORICAL QUESTION (We'll let you have more than 2 seconds tho' )
Personal emails is not a good option, as it deprives others of the opportunity of learning from our mistakes. We should keep everything in the open, and besides IM is probably not very viable, given time zone differences (compare my "Registered" date in this message header with say Todd's)
Bruce
> Jamie, I think you'd like subversion, is there
> anything to stop you changing / learning it?
keeping me from changing? no, i'll use whatever suits
stopping me from learning? only not trying
> scale regarding testing, I think automated tests are
> a good idea here.
can't be junit, it presupposes you can compile
how do you junit test a compiler check?
maybe we can use ant to automate a test/build suite with pass/fail classes
> Regarding comms channels, rapt has a mailing list
> and a wiki which can both be used, but we need to
> consider that there may be other people actively
> following this thread here, and we need to consider
> them as well.
sounds good, collaborate on the wiki, offer link here for others to follow
> besides IM is probably not very viable, given time
> zone differences (compare my "Registered" date in
> this message header with say Todd's)
>
> Bruce
fyi, i am east coast, usa
bruce, kiwi?
todd?
> > Jamie, I think you'd like subversion, is there
> > anything to stop you changing / learning it?
>
> keeping me from changing? no, i'll use whatever
> suits
> stopping me from learning? only not trying
>
OK,I'll try and convert to svn, once I can find the instructions again.
> can't be junit, it presupposes you can compile
> how do you junit test a compiler check?
> maybe we can use ant to automate a test/build suite
> with pass/fail classes
JSR-199 developed javax.tools package, and using that I create an in memory file system which I write my source file into in my test case, I also attach a messager that collects the compiler error messages, and then invoke the compiler programatically, then when its finished I check the messager to see what compiler messages it has collected, and assert on those. Relatively painless as all the grunt is in a superclass which I subclass for each test suite, and the tests are quite readable. Using rapt's @LongString for larger static blocks of source code makes them even better ;)
>
> > Regarding comms channels, rapt has a mailing list
> > and a wiki which can both be used, but we need to
> > consider that there may be other people actively
> > following this thread here, and we need to
> consider
> > them as well.
>
> sounds good, collaborate on the wiki, offer link here
> for others to follow
That's my thoughts, and use mailing list for whatever discussion where the wiki isn't quite so appropriate.
Bruce
Lets see...
The annotation processor cannot see the name of the superclass nor its type elements...
I don't think that is a bug as we have not developed the processor yet (lol).
So all you silent observers, would you object if we took this discussion elsewhere and posted a link to it in this forum?...
I don't care what the observers think, I say we move it else ware, those reading, who cant follow a link do not need to be involved.
Personal emails is not a good option...
Good point, as long as we have a way to communicate, I am good. I vote wiki, thats much nicer...
can't be junit, it presupposes you can compile...
Untrue!!! JUnit is perfect, we do not want all of our tests to pass until it actually works.
Example test (excuse the ugly/psudo code):
// bla...
public CompilerTest extends CompilerTestGen<TestCase> implements Compareable {
public void testCompareableCompiles() {
Compareable x = new Compareable() {
@Delegate Compareable.class
public Compareable getCompareable() { // Bla... }
}
}
}
CVS/SNV...
If someone needs help with SVN, based on what they would do in CVS, let me know (I will help), but if its a big deal, I am good with CVS.
Let me know if there is anything here you disagree with, or do not understand.
Also, I think we need to take a vote on where this thread will go...
I abstain
Todd
The RAPT project at java.net seems to be closed to me, any other suggestions as to where we can do development?Todd
Todd and Jamie should now be set up as content developers on rapt. I'll set you up as code developers once I sort out the CVS / SVN issue, and rearrange the src directory structure to support th old JDK5 apt based stuff and the newer JDK6 javac based stuff side by side.
This thread now evolves here http://wiki.java.net/bin/view/Javatools/DelegateHead at least for the next while.
We might post post occasional progress reports back in this thread.
Just a heads up on a naming issue.
I saw in another discussion on a similar subject that someone made the point that delegation isn't strictly the right term for what you are proposing (if I understand correctly).
According to this definition, delegation is when an object passes on some of its workload to a second object, but with the proviso that the second object knows the identity of the object using it (eg it is passed a reference to it).
When the second object does not necessarily know the identity of the object using it then the process is more properly termed forwarding.
I've checked this out and there does seem to be some basis for it.
Given that this annotation might become widely used, I thought it might be as well to get the name right before it gets cast in stone.
Jonty
Thanks Jonty,
Whether a class is knowingly or unknowingly (or actively or passively) taking part in a pattern is a useful attribute for identifying what the pattern is.
What you are saying seems right to me.
We do have a pretty clear idea on how this will work, but poor naming can cripple an API at least as much as poor implementation, and I can't say I have given naming much thought so far, having just gone along with Todd's original post.
Do you have references from your "checking this out"?
Comments Todd?
Bruce
> Do you have references from your "checking this out"?
I can't remember what I looked at at the time, but having a quick look on the web there's:
http://en.wikipedia.org/wiki/Delegation_%28programming%29
which states:
This mechanism is sometimes referred to as aggregation, consultation or forwarding (when wrapper object doesn't pass itself to wrapped object [Gamma98, p.20]).
Also:
http://www.javaworld.com/javaworld/javaqa/2001-09/01-qa-0914-delegate.html
which states:
True delegation is a bit more rigorous. In true delegation, the object that forwards the request also passes itself as an argument to the delegate object, which actually does the work.
That said, delegation doesn't seem to be a term with a tightly defined generally accepted definition. For example Microsoft uses the term differently (extraordinary that...) for a type of function closure.
Forwarding does seem to be the more correct term, but delegation is probably not so wrong that it would create problems with acceptance. Although delegation is probably the word most programmers would come up with to refer to this action, forwarding does seem to be a pretty self explanatory term too. You pays your money you takes your pick I guess. Personally I think it would be best to follow Gamma et al and use forwarding.
Jonty
jontyla:
I don't know how credible your sources is, but it is pretty clear the Delegation refers to ANY place where an object uses another Object to do its work in the GOF book.
If you can show a more credible source, I would be happy to change with the times.
As for how rigorous the definition is, it isnt... Delegation from my understanding is a generic term, referring to ANY situation where this kind of thing occurs, hence the ambiguity.
Thanks for the good eye, pleas let us know if we are using any other names improperly.
Todd
PS: Forwarding is also mentioned GOF in it but in the context of where you pass a "this" reference, so are you sure you don't have it backwards?
Also, I am still unable to login!!!
>I don't know how credible your sources is, but it is pretty clear the Delegation refers to ANY place where an object uses another Object to do its work in the GOF book.
Well, I did quote the sources. Still, if you feel wikipedia is not to be trusted in quoting Gamma,I now have my copy of Gamma et al 1995 open in front of me, and here is a quote from section 1.6, page 21, 'delegation':
"Sometimes the Mediator object implements operations simply by forwarding them to other objects; other times it passes along a reference to itself and thus uses true delegation. Chain of Responsibility (223) handles requests by forwarding them from one object to another along a chain of objects. Sometimes this request carries with it a reference to the original object receiving the request, in which case the pattern is using delegation."
Also, on the preceding page:
"To achieve the same effect [as inheritance] with delegation, the receiver passes itself to the delegate to let the delegated operation refer to the receiver."
Given this, I don't see how you can justify saying what you do about the GOF book (unless they have changed their minds about the definition in a later edition of the book than mine, in which case provide a contradictory quote).
As a further source, Effective Java by Joshua Bloch (principle engineer at Google and previously distinguished engineer at Sun) 2001 says (page 75):
"Sometimes the combination of composition and forwarding is erroneously referred to as delegation. Technically, it's not delegation unless the wrapper object passes itself to the wrapped object."
He does cite the GOF book as the reference for this, so it might not be considered enough of an independent source, though my guess is that this definition of delegation is the one used in the academic world and that is why he uses it (I don't read OO academic conference proceedings so I don't know if this guess is right).
Anyway, all that and the original post that alerted me to the issue (no I can't remember where it was, though possibly somewhere on one of the sun forums a few years back) provide support for what I was saying. Still, if you can provide counter examples or if you can make a case for ignoring this and going with the name delegation in spite of it, feel free. I was merely pointing out that there is an argument to be made for calling it forwarding rather than delegation, and I can't find any credible source for the contrary other than that many programmers aren't aware of the distinction and find delegation a nicer name (which I do in fact).
Jonty
my interest in this thread being annotated-design-pattern-implementations, i propose we provide both @Delegate and @Forward annotation variants.
> i propose we provide both @Delegate and @Forward annotation variants
'Twould be nice, but providing this mechanism for true delegation would be a bit difficult. The problem is that you don't know how the delegate gets to know about the delegator. It might be that the delegate receives the delegator in its constructor, or it may receive it on a method by method basis in one of its parameters, or even by some other mechanism (such as receiving it in one method call and storing it for use in a later call, or automatically because it is a non static inner class).
I can't think of any simple way to use annotations to achieve this because there are too many possibilities. However for the cases where the delegator is stored in the delegee via the constructor or via a setter method or because it's an inner class then delegation will look just like forwarding and so the same mechanism will work unchanged. If you can propose a good way of achieving more general delegation then that would be great.
The question to answer if you want @Delegate as well as @Forward is "what does @Delegate do that @Forward doesn't?".
Jonty
jontyla:
Having re-read the GOF book (now at my desk at work for this thread), I now understand the confusion with you, and the sun guy.
Basically what I can gather is this...
"True Delegation" is defined (as best as I can understand) using another object to do the work "this" object requires, PROVIDING a reference "back to" this.
"Forwarding" is a new (well not that new) term that SEEMS to be coined in the GOF book. It seems to be defined as (as best as I can understand) using another object to do the work "this" object requires, WITHOUT providing a reference "back to" this.
"Delegation" is referred to in many areas, and sited in works by more then just the GOF, in fact the best I can come up with the term was coined in SmallTalk, but I digress. It is defined as (as best as I can understand) using another object to do the work "this" object requires, with OR without providing a reference "back to" this.
Having said that, "as best as I can understand", should be emphasized.
IF I am correct, the current term is the proper one, as we MAY eventually support "True Delegation" although it is currently out of scope.
IF I am not correct in ANY way, please let me know.
Todd
PS: please move responses to...
http://wiki.java.net/bin/view/Javatools/DesignConcerns
Message was edited by:
musheno
I thought that it might be useful to have some other opinions on this so I have posted a question to the Patterns and OO Design forum ( http://forum.java.sun.com/thread.jspa?threadID=5187279 ).
> IF I am not correct in ANY way, please let me know
well...
http://en.wiktionary.org/wiki/forward defines forward as:
To send (something received) to a third party
In the context of OO it seems to be self explanatory: you pass on the message/method call to another object to deal with. Whether this object is a delegate or not is irrelevant. I don't see any reason at all to support the idea that an object you are forwarding to must necessarily not have a reference back to the caller (as you suggest).
For me, if a method consists of 'void m() { d.m(); }' then this method is forwarding the request to d. If d happens to know who its caller is then this is also delegation, otherwise it isn't (according to the strict definition). Forward is a more inclusive name.
That's the rational for using the name Forward rather than Delegate.
That said, this discussion seems to be rapidly disappearing down a rat hole.
@Documented
@Retention( RUNTIME )
@Target( { FIELD } )
@Inherited // how does this apply for FIELD?
public @interface Forward {
/** A list of <B>interfaces</B> that the annotated field will be used to implement. */
public Class[] interfaces();
/** Flag if a reference to the object instance with this annotated field is wanted */
public boolean delegate() default false;
}
comments...
> [code]
> @Documented
> @Retention( RUNTIME )
> @Target( { FIELD } )
> @Inherited // how does this apply for FIELD?
"Note that this meta-annotation type [@Inherited] has no effect if the annotated type is used to annotate anything other than a class. Note also that this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect."
http://java.sun.com/javase/6/docs/api/java/lang/annotation/Inherited.html
> public @interface Forward {
> /** A list of <B>interfaces</B> that the annotated
> d field will be used to implement. */
> public Class[] interfaces();
>
> /** Flag if a reference to the object
> instance with this annotated field is wanted */
>public boolean delegate() default false;
> de]comments...
> /** Flag if a reference to the object instance with this annotated field is wanted */> public boolean delegate() default false;Yes, but what does it mean? What effect will it have on the generated class?