The Artima Developer Community
Sponsored Link

Weblogs Forum
Traits for Java

20 replies on 2 pages. Most recent reply: Jun 11, 2010 10:33 PM by Howard Lovatt

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 20 replies on 2 pages [ 1 2 | » ]
Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Traits for Java (View in Weblogs)
Posted: Dec 15, 2007 6:47 PM
Reply to this message Reply
Summary
There is a lot of interest currently in the ability to extend a class (or something close to class extension) without requiring recompilation or source changes, Traits offer a viable method of achieving this.
Advertisement

What are Traits?

Traits are like interfaces, but can have a method body and a class that implements a Trait inherits the method body. In many ways Traits are similar to abstract classes, but they don't have constructors, don't have initializers, don't have fields, can be multiply inherited (like an interface), and super does not refer to a method in an inherited trait, instead of super you have to qualify the intended name [TraitName].[methodName]( ... ).

By way of example consider current best practice: you write an interface, e.g. List, write a useful abstract class to make writing lists easier, AbstractList, and then implement some lists, e.g. ArrayList. This is all well and good, but suppose you now want to add some extra methods to List, e.g. sort. You can't because every class that implements List would need to add a sort method and you don't have control over all the source that uses List. So instead you use a helper class, Collections, and add sort as a static method. The user then calls sort( list ) (assuming a static import is used). A better technique is a Trait, with a trait you modify List:

    public interface List extends Collection {
      ...
      void sort() { ... } // same syntax as an interface but allows method bodies
    }

ArrayList now automatically pick up sort and the user does not need a special import, instead just like any other instance method the user writes list.sort().

Details

With Traits, if there is a conflict due to multiple inheritance then you have to resolve this conflict (this is a key difference from Mixins - see below). E.G.:

    interface X { int m() { return 1; } }
    interface Y { int m() { return 2; } }
    interface Z extends X { int m() { return 3; } }
    class XY implements X, Y { // conflict - 2 m's
      public int m() { return X.m(); } // resolve conflict
    }
    class XZ implements X, Z {} // no conflict - Z's m overrides X's

Note on Mixins

The difference between a Trait and a Mixin is that order is important. In the class XY example given above, with a Trait you have to explicitly say which m you want. With a Mixin the order in which the interfaces are mixed in determines what happens. class XY implements X, Y {} is read as first mixing in X then mixing in Y; hence the m from Y overrides the m from X, since it is mixed in afterwards.

Note on Extension Methods

An alternative way of adding methods to a class is Extension Methods, these are proposed for Java 7, but have a number of problems.

Extending existing classes

To be able to add methods to an interface without recompiling, all clients of the interface, the class loader has to be extended to automatically add in the extra methods if they are not present and to flag an error if an unresolved conflict exists. Currently the class loader flags a missing method, the required extra action would be to see if the missing method is in an implemented interface and if it can be added without conflict.

As an alternative to abstract classes

Traits would be a viable alternative to abstract classes in many cases, for example the methods in AbstractCollection could be moved to Collection and no one would need to use AbstractCollection in the future. But a Trait cannot have any fields, therefore some abstract classes, e.g. AbstractList (it contains a field) would still be needed, although some methods may be moved to an interface.

Conclusions

I think Traits would be a useful addition to Java, what do others think?


Morgan Conrad

Posts: 307
Nickname: miata71
Registered: Mar, 2006

Re: Traits for Java Posted: Dec 17, 2007 12:09 PM
Reply to this message Reply
I like this much better than the weird static import stuff. It closesly matches my proposal in one of the first Artima "Extensions ... " discussions.


However, I'm wondering just how bad it would be to do absolutely nothing to the Java syntax, and

1. Add sort() to the Collections interface.
2. Implement sort() in AbstractCollections via a trivial call to Collections.sort().

And STOP.

Of course, all code that implements MySpecialList that doesn't extend AbstractList will break. The counter arguments are:

1) Just how many cases of this are there?
2) Those guys can implement sort() with one line of code.
3) If all else fails, those guys can stick to Java 6.


Obviously, I've picked a very simple example, and "traits" would often be used in more complex cases. But the need to resolve multiple inheritance-like conflicts adds complication, maybe it's not worth it?

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Traits for Java Posted: Dec 17, 2007 3:46 PM
Reply to this message Reply
@Morgan,

Yes - I agree that is a possibility. Not sure how much code would be broken (some of mine would - but not much).

Another possibility that I have seen other people suggest is versioning the interfaces, e.g.:

interface List2 extends List { void sort(); }
 
abstract class AbstractList extends AbstractCollection implements List2 {
  ...
  public void sort() { Collections.sort( this ); }
}


This way you are very unlikely to break existing code. It is possible that someone extended AbstractList and added a sort with a different signature, so there is still a chance of breaking old code.

The main downside of versioning is a lot of refactoring as people have to change from List to List2 to pick up the new features.

So yet another possibility is to add a source keyword that allows for versioning, e.g.:

source 7;
 
interface List {
  ...
  @Version(7) void sort();
}
 
abstract class AbstractList extends AbstractCollection implements List {
  ...
  @Version(7) public void sort() { Collections.sort( this ); }
}


The methods marked with @Version(7) are only available in files that have a source 7 statement or above at the start. That way you don't need to refactor.

Wilfred Springer

Posts: 176
Nickname: springerw
Registered: Sep, 2006

Re: Traits for Java Posted: Dec 18, 2007 12:37 AM
Reply to this message Reply
Let's keep Java Java and move to Scala if we want traits.

Carsten Saager

Posts: 17
Nickname: csar
Registered: Apr, 2006

Re: Traits for Java Posted: Dec 18, 2007 10:46 AM
Reply to this message Reply
Traits (or simply implementations in interfaces) had been already rejected in the JCP. There is little chance that they will make it into the language.

It is though possible to mimick traits, even in an open class manner, with Java. I wrote Open-Traits http://code.google.com/p/open-traits/ some weeks ago - it is not finished but demonstrates well the use and the caveats of this technique

Bruce Fancher

Posts: 4
Nickname: iterative
Registered: Mar, 2007

Re: Traits for Java Posted: Dec 18, 2007 2:31 PM
Reply to this message Reply
It's nice to see that the Java mainstream is finally starting to catch up with additions that Objective-C programmers proposed adding nine years ago:

http://www.stepwise.com/Articles/Technical/JavaCats.html

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Traits for Java Posted: Dec 18, 2007 11:44 PM
Reply to this message Reply
@Wilfred,

This proposal actually goes a little beyond what you can do in Scala. In particular it allows new methods to be added to a Trait (interface) without having to recompile code that uses that interface.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Traits for Java Posted: Dec 19, 2007 12:07 AM
Reply to this message Reply
@Carsten,

great project - good luck with it.

The main difference between the my proposals is the language extension would enable compile time checks whereas your proposal would be runtime. To some extent your FixedTrait addresses this balance. There would still be some limitations. EG1 class with constructor arguments:

interface Addable {
  int get();
  Addable instance( int value );
  Addable add( int increment ) { return instance( get() + increment ); }
}
 
class Adder implements Addable {
  private final int value;
  Adder( int value ) { this.value = value; }
  public int get() { return value; }
  public Adder instance( int value ) { return new Adder( value ); }
}


The problem with the above is the constructor with an argument for the class that implements the Trait. The Trait doesn't have a constructor with an argument, but the implementing class does!

EG2 different types:

Object trait1 = new Traits().with( new CU() ).traited();
Object trait2 = new Traits().with( new CU() ).traited();
 
trait1.getClass() != trait2.getClass();
trait1 instanceof ?


This you might be able to circumvent to some extent by returning a class instead of an instance of a class. But it would be hard to do 1 and 2 simultaneously.

I am not saying your project isn't valuable - I think it is great. But Traits are an example were an API isn't sufficient - you need a language construct.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Traits for Java Posted: Dec 19, 2007 12:09 AM
Reply to this message Reply
@Bruce,

I am well aware that other languages, particularly dynamic languages, have a Traits or similar since Lisp days. The twist I am adding is allowing the loader to extend classes, thereby giving some dynamic language flexibility to a statically typed language.

Jesse Kuhnert

Posts: 24
Nickname: jkuhnert
Registered: Aug, 2006

Re: Traits for Java Posted: Dec 20, 2007 12:28 PM
Reply to this message Reply
I love it, too bad the JCP rejected it from what was said in the comments...

Jesse Kuhnert

Posts: 24
Nickname: jkuhnert
Registered: Aug, 2006

Re: Traits for Java Posted: Dec 20, 2007 7:39 PM
Reply to this message Reply
Hey - wait a minute. This sounds eerily familiar to the way laws get passed in american politics. Nah, I'm just being paranoid.

> I love it, too bad the JCP rejected it from what was said
> in the comments...

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Traits for Java Posted: Dec 20, 2007 8:23 PM
Reply to this message Reply
@Jesse,

Thanks for saying you like the Traits suggestion. Some things have got up in Java after initial rejection - so you never know. Personally I think it is unlikely that anything will get up that isn't backed by a big company. But it is good fun interacting with people who have great ideas.

Ian Marteens

Posts: 1
Nickname: imart
Registered: Jan, 2008

Re: Traits for Java Posted: Jan 4, 2008 1:29 PM
Reply to this message Reply
I think it would be a useful feature. C# already does the same with extension methods, but "traits" have a nicer syntax and avoid some versioning problems extension methods have.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Traits for Java Posted: Jan 4, 2008 3:29 PM
Reply to this message Reply
@Ian,

Thanks for saying you like the traits proposal. I to think it is better than extension methods which I discussed in this blog:

http://www.artima.com/forums/flat.jsp?forum=106&thread=220783

and highlighted some of their problems. I didn't include versioning problems though - can you elaborate?

qinxian xiang

Posts: 5
Nickname: moonlight
Registered: Dec, 2006

Re: Traits for Java Posted: Jan 29, 2008 9:49 AM
Reply to this message Reply
> <pre>
> interface X { int m() { return 1; } }
> interface Y { int m() { return 2; } }
> interface Z extends X { int m() { return 3; } }
> class XY implements X, Y { // conflict - 2 m's
> public int m() { return X.m(); } // resolve
> esolve conflict
> }
> class XZ implements X, Z {} // no conflict - Z's m
> 's m overrides X's
> </pre>
>

Hi, that seems be problem.
xy=XY();
now take xy as X, I hope call X(xy).m();
but after a smoking, I hope call Y(xy).m();
According to X Y interface protocol, I should get 2 m();
The above declaration seems not very clear?
I seem catch some thing from memory, eiffel get a not bad solution for MI. maybe I should check it again.

Flat View: This topic has 20 replies on 2 pages [ 1  2 | » ]
Topic: Creating a Domain Specific Language with Groovy Previous Topic   Next Topic Topic: EuroPython 2010

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use