JDK-6469144 : RFE: consider adding traits to Java
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Affected Version: 6
  • Priority: P5
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2006-09-11
  • Updated: 2011-10-31
  • Resolved: 2011-10-31
Related Reports
Duplicate :  
Description
A DESCRIPTION OF THE REQUEST :
Traits are a method similar to mixins which allows multiple inheritance of implementation without inhertiting state. in a Java context Traits may be thought of as interfaces which contain an implementation, or classes with no state.

It seems to me that traits as used in the examples below allow both better reuse of implementation and most of the benefits of closures and curried functions without much added baggage and a reasonable syntax.



Fortress: http://research.sun.com/projects/plrg/fortress.pdf
Traits: A Mechanism for Fine-grained Reuse  - http://www.iam.unibe.ch/~scg/Archive/Papers/Duca06bTOPLASTraits.pdf

  From the above paper
  ���Two roles are clearly separated: traits are purely units of reuse, and classes are generators of instances.
  ���Traits are simple software components that both provide and require methods (required methods are those that are used by, but not implemented in, a trait).
  ���Classes are composed from traits, in the process resolving any conflicts, and possibly providing the required methods.
  ���Traits specify no state, so the only conflict that can arise when combining traits is a method conflict. Such a conflict can be resolved by overriding or by exclusion.
  ���Traits can be inlined, a process that we call ���flattening���: the fact that a method originates in a trait rather than in a class does not affect the semantics of the class.
  ���Difficulties experienced with multiple inheritance disappear with traits, because traits are divorced from the inheritance hierarchy.
  ���Difficulties experienced with mixins also disappear, because traits impose no composition order.


Trait composition respects the following three rules:
  ���Methods defined in a class itself take precedence over methods provided by a trait. This allows glue methods defined in the class to override methods with the same name provided by the traits.
  ���Flattening property. A non-overridden method in a trait has the same semantics as if it were implemented directly in the class.
  ���Composition order is irrelevant. All the traits have the same precedence, and hence conflicting trait methods must be explicitly disambiguated. A conflict arises if we combine two or more traits that provide identically named methods that do not originate from the same trait. Conflicts are resolved by implementing a glue method at the level of the class, which overrides the conflicting methods, or by excluding a method from all but one trait. In addition traits allow method aliasing; this makes it possible for the programmer to introduce an additional name for a method provided by a trait. The new name is used to obtain access to a method that would otherwise be unreachable
because it has been overridden. As we shall see, method aliasing offers a less fragile solution to this problem than method renaming.

As well as those characteristics of traits taken from the above paper, I have added a couple of extra rules:

  ���Traits can have parameters (as in examples 1&3) if these match the parameter names in the methods defined in the trait then those methods are curried with that parameter if a formal parameter exists.

  ���Traits with a method named the same as the trait (except for a lower case initial letter) may be used in a similar way to closures, see examples 3,4&5,
example 5 shows three possible ways of accessing local variables, the last way is the most like a classic trait, the first the most like a classic closure.

  ���As the trait methods are introduced into the class, access modifiers on the trait matter if they are none abstract (see rename in example 2).


Example 1

public trait Errors(String component) {
    void warning(String component, String warn) { System.out.println("Warning - " + component + ": " + warn; }
    void error(String component, String err) { System.out.println("Error - " + component + ": " + err; }
}


public class X traits Errors("X") {
     public void x(String y) {
           if (y==null) { warning("should not be null"); }    // prints out "Warning - X: should not be null");
     }
}


Example 2

public trait A { String a() { return "a"; } }
public trait B { String b() { return "b"; } }
public trait C extends A,B { String c() { return "c"; } }
public trait A2 { String a() { return "A"; } }
public trait B2 { private String b() { return "B"; } }

public class X traits A, B, C, A2, B2 {
     remove A2.a;
     rename B2.b to d; // this method is private to class X

     public void test() {
          System.out.println(a() + b() + c() + d() );  // prints "abcB"
     }
}


Example 3

public trait A(String x, String y) { abstract String a(String x, String y) ;  }

public class X {
    public void test(A a) {
        System.out.println("2 params: "+a.a());
    }
    public void test(A(String x) a) {
        System.out.println("1 param: "+a.a("a"));
    }
    public void test(A(String x, String y) a) {
        System.out.println("no params: "+a.a("a","b"));
    }
    public static void main(String[] argv) {
        test( A("p","q") { return x + y; });   // prints "2 params: pq"
        test( A("r") { return x + y; });   // prints "1 param: ar"
        test( A() { return x + y; });   // prints "no params: ab"
    }
}


Example 4

public trait Run implements Runnable { abstract void run(); }

public class X {
    public static void main(String[] argv) {
        // print "done" in a new thread
        new Thread( Run() { System.out.println("done"); }).start();
    }
}


Example 5

// perhaps we could do this (with cleaver synthesis of the desired methods behind the scenes):
public trait Test { String test() { return a+b+c+d; } }

public class X traits Test {
    private String a = "a";
    private String b = "B";
    public class Y {
        String b = "b";
        public void doIt(String c) {
            int d = 1;
            // print "abc1"
            System.out.println(test());
        }
    }
    public static void main(String[] argv) {
        Y y = new Y();
        y.doIt("c");
    }
}

// perhaps we could do this (with cleaver synthesis of the desired methods behind the scenes):
public trait Test {
    abstract String getA();
    abstract String getB();
    abstract String getC();
    abstract int    getD();
    String test() { return getA()+getB()+getC()+getD(); } }

public class X traits Test {
    private String a = "a";
    private String b = "B";
    public class Y {
        String b = "b";
        public void doIt(String c) {
            int d = 1;
            // print "abc1"
            System.out.println(test());
        }
    }
    public static void main(String[] argv) {
        Y y = new Y();
        y.doIt("c");
    }
}

// or maybe it would have to only use defined getters and setters
public trait Test {
    abstract String getA();
    abstract String getB();
    String test() { return getA()+getB(); }
}

public class X traits Test {
    private String getA() { return "a"; }
    private String getB() { return "B"; }
    public class Y {
        String getB() { return "b"; }
        public void doIt(String c) {
            int d = 1;
            // print "ab"
            System.out.println(test());
        }
    }
    public static void main(String[] argv) {
        Y y = new Y();
        y.doIt("c");
    }
}

This is only a design sketch of how traits may be added to Java. Traits could I suppose be added without any new keywords by making methods in interfaces be able to have bodies and overloading some syntax to allow renaming and removal of unwanted traits, however this would make the langage harder to read. I cannot see any adverse interactions between generics and traits. Traits used as closures would require some sythesis of code behind the scenes to allow an object of a compatible class to be constructed and used at the calling site. Something like:

Example 6

public trait Run implements Runnable { abstract void run(); }
public class X {
    public class $Run implements Runnable {
        public void run() { System.out.println("done"); }
    }
    public static void main(String[] argv) {
        // print "done" in a new thread
        new Thread( (new $run()).run() ).start();
    }
}



JUSTIFICATION :
Traits allow reuse of implementation in a better way than multiple inheritance or mixins, currently Java does not allow such reuse, the proposed language enhancement to include closures would allow some reuse of implementation.

It seems to me that traits can be used to allow reuse of implementation and most of the benefits of closures and curried methods without much added baggage and a reasonable syntax.

Comments
EVALUATION A fine language feature, but not one likely to be implemented soon.
20-10-2006