Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[aspectj-users] Trait syntax sugar?

Hi all,

I've used a common ITD pattern for years now to introduce interface implementation(s) into existing classes when I have well-encapsulated, orthogonal state & behavior that I want to introduce.  Here's a simple example (hand-written, but gives you the gist):  suppose several of my domain entities need to have a string property "name".

First, define an interface that describes the behavior:

package org.example.traits;

public interface Nameable {
    String getName();
    void setName(String name);
}

Next, define an annotation that can be used to drive introduction:

package org.example.traits;

@Target(TYPE)
@Retention(RUNTIME)
public @interface Expresses {
    Class[] value();
}

Next, define an aspect that provides an implementation of the behavior:

package org.example.traits;

public privileged aspect NameableAspect {

public interface I extends Nameable {
}

declare parents : (@Expresses(..,Nameable.class,..) *) implements I;

private String I.name;

public void I.setName(String name) {
this.name = name;
}

public String I.getName() {
return name;
}
}

Last, annotate a class that needs to receive the ITD:

package org.example.domain;

// imports...

@Expresses(Nameable.class)
public class Person {
}

After considering JDK8's default methods feature and comparing it to Scala's traits and mixins, I realized that AspectJ, with a little syntax sugar, could have the same features as Scala.  The above example could be rewritten as the following, depending on how many new keywords were introduced.

First, define the trait's state & full or partial behavior using a proposed new keyword "trait":

package org.example.traits;

trait Nameable {

    String name;

    void setName(String name) {
        this.name = name;
    }

    String getName() {
        return name;
    }
}

Here is what ajc would do with the trait:
  • produce a public interface with the same name as the trait with the trait's methods, and
  • define an aspect that
    • introduces the trait's fields and the implementations of the trait's methods into the produced interface, and
    • uses @Expresses to drive a declare parents statement to mix in the implementation into classes that are annotated with @Expresses.
With this syntax, a user could simply declare that a class needs to express the trait:

package org.example.domain;

import org.example.traits.Nameable; // interface generated from trait

@Expresses(Nameable.class)
public class Person {

    public Person(String name) {
        setName(name);
    }

    // ...
}

In summary, this proposal is just syntax sugar over features that are already present in AspectJ for quite a while.  It would be nice, as part of this enhancement, if https://bugs.eclipse.org/bugs/show_bug.cgi?id=288282 could be resolved so that the ajc-generated, mangled accessors & mutators aren't present in the mixed in class (because https://bugs.eclipse.org/bugs/show_bug.cgi?id=73507 is now fixed).

WDYT?

-matthew

-- 
mailto:matthew@xxxxxxxxxxxxxxx 

Back to the top