Declared properties and unsigned types are relatively small changes. BTW, in JDK8 they have provided limited support for unsigned types, just not at the language level:
They also don't require you have access to the source (anybody can write an extension method on System.Collections.IEnumerable, not just the BCL team). That they work on any type (not just interfaces) also makes them considerably more general than default implementations.
I also find the idea of an interface carrying implementation a little weird, the abstract class/interface line gets a lot blurrier.
That said, lambda support that only worked on new code would be pretty lackluster; so it's good some thought has gone into getting them into legacy code.
Look at System.Core's Count() inside reflector. Using pseudo code:
def count(ref):
if ref is ICollection:
return (ref As ICollection).Count
else
n = 0
for each item in ref:
n++
return n
That hurts badly if you are not careful. Enumerables will be iterated and collections will return length so when you call it you don't know for certain if it's O(N) or O(1). If someone has downcast ICollection<T> to IEnumerable<T> then you really don't know where you stand.It's shit like that which is hard to debug and brings your system to its knees.
For example (disclaimer, it's been a bit since I've touched these classes; but you should get the idea), an extension of Iterator that adds a longSize() method would probably want to make use of ArrayList's size() rather than iterating over the whole thing too.
As an aside, if your code absolutely and utterly depends on a certain operation's runtime performance you had better be using a type that actually guarantees that performance. List<T> guarantees Count is O(1) ( http://msdn.microsoft.com/en-us/library/27b47ht3.aspx ) , IEnumerable<T> doesn't ( http://msdn.microsoft.com/en-us/library/bb338038 ); that's your bug.
Scala solves this problem nicely with traits, btw.
In terms of .Count() which will attempt to call ICollection.Count you can create the same thing as an extension method or a default implementation on Enumerable.
Either way you do it you will have ambiguity regarding time complexity.
Extension methods have the advantage that the langauge designers _could_ have left .Count() off IEnumerable and then people who wanted to use it could implement it themselves and those worried about the ambiguity wouldn't have to risk people enumerating their collection needlessly.
The problem with extension methods over default implementation interfaces is that the code regarding a class can be distributed in several (potentially non-obvious) locations and it reduces portability.
The advantage is that you can extend a class or an interface you don't own, and you can have implementations specific to a given namespace.
So for a (poor) example you might extend float so you could convert an angle to a 2d unit vector, but only in given bits of your program you want to be converting between angles and vectors.
Isn't that exactly what we're talking about?
The huge difference between C#'s extension methods and traits is that the first one is statically dispatched, the second one dynamically.
This means classes with a better implementation can just override Count() and suffer none of the problems of the extension method approach.
Scala also offers implicits to "enrich" existing types, slightly comparable to Extension methods, but a lot more general.
Whereas extension methods only support some ad-hoc "addition" of members to existing types, Scala's implicits allow you to use these methods to implement new interfaces -- and isn't that the reason why you add methods in the first place: to implement interfaces?
> So for a (poor) example you might extend float so you could convert an angle to a 2d unit vector, but only in given bits of your program you want to be converting between angles and vectors.
implicit def floatTo2DUnitVector(angle: Float) = 2DUnitVector(...)
Done. Import where you need it.Btw, C# has some limited form of implicits, too, but I haven't seen any serious usage of them for a while.
Instance methods are bound before extension methods, if someone provides an implementation of Count() in a subclass it'll be invoked instead (provided your reference is typed appropriately). More narrow extension methods are bound before general ones, so you can even "override" within extension methods.
Scala has some neat stuff, but it's not Java 8 so I don't see how it's relevant.
> Btw, C# has some limited form of implicits, too, but I haven't seen any serious usage of them for a while.
C# has implicit conversion operators you can define, http://msdn.microsoft.com/en-us/library/z5z9kes2(v=vs.100).a... . You'd better have a really good reason for defining one though, silent conversions are generally frowned upon.
> Scala has some neat stuff, but it's not Java 8 so I don't see how it's relevant.
Java's default methods are basically Scala's traits (just made a bit more cumbersome to make Java developers feel right at home), so what I said about traits applies to “interfaces with default methods”, too.
The required exception handling can go next because it doesn't make sense if you version and add new exceptions. Your complier required exception handling was just thrown out.
That was bugging me too, thanks for the explanation.
My problem is the same was said for implementing Generics with type erasure. Doesn't make it a good feature for anyone starting a new project. I wish when they soiled the language for compatibility reasons that they would make it an "opt-in" feature.
As far as I can tell, interface defaults don't provide much value over compatibility.