Now Java is the king of obviousness; it's very clear exactly what is being done on every line of code. It pays for it in being very wordy. I don't like inheritance because it breaks obviousness; it's no longer obvious where a virtual call is actually going to be served. With composition, the immediate object has to serve the call, even if it's just to delegate it. The only virtual calls would be dispatching calls to interface methods. And this style isn't expensive after JIT; inlining these kinds of delegations is its primary optimization. (Think getter-method chains that return a field value, a common pattern in Java.)
Lambdas were a step towards terseness, but they sacrifice obviousness to get there. It hides things... What SAM type is a lambda implementing? You have to check to call site; the lambda itself won't tell you. The anonymous class syntax tells you exactly what is being implemented, but that means you have to type more to get there.
Now, I like properties because IMO they optimize both obviousness and terseness. You are obviously making a single property with a getter and a setter, as opposed to defining each separately. And you are more terse by removing the need to define each separately.
That's kind of the point of generics? I don't understand this argument at all. If you need a specific kind of type, then bound the generic using `extend` and you can use the bounding type's methods and fields. [0]
You're still using a different definition of obvious, despite my having defined it for this use case. For instance:
> Why should be obvious to call stream() on an object that is already a collection?
That's the very definition of obvious. You are making it quite clear that you intend to use this collection as a stream. It's extremely obvious. Same with the buffered reader; Readers are unbuffered, and it's very obvious that you are buffering the reader when you wrap it. (Also, Java has `java.nio.Files.readAllLines` [1], so I'm not sure what you're going on about.)
Extention methods are very non-obvious; where is an extension method defined? The answer is anywhere; it can live in any type you imported. It's not obvious looking at the line of code where execution is going to flow. (And yes, an IDE will track it down for you. Just because an IDE can do the work for you, doesn't mean that there's not work to be done.)
[0] http://docs.oracle.com/javase/tutorial/java/generics/bounded...
[1] http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files...
> The stream is a very well known and completely different concept and it is widely used in reactive programming, they just stole that concept to use it in a completely non obvious way.
Java streams seem to meet the ideas of stream processing to me. Given a sequence, apply transformations in order, possibly in a transparently parallel fashion. Compare the Wiki definition with the `BaseStream` interface documentation:
https://en.wikipedia.org/wiki/Stream_processing
http://docs.oracle.com/javase/8/docs/api/java/util/stream/Ba...
> Finally extension methods are perfectly obvious. They are just syntactic sugar for static methods, I hope that at least you agree that static method are the gist of obviousness.
Yes, I of course agree. My problem is that extension methods hide where they live, by pretending to live on the type. And to be fair, I have the same issue with static imports in Java. When I look at a function or method call, it should be immediately apparent where control flow is going to go. That's not the case with either static imports or extension functions; I have to resolve the method name first.
This was one of things that always bothered me about C#, What is the point of properties. What do the offer that public fields don't?
What do getters and setters offer that public fields don't? Properties offer exactly the same advantage as getters and setters, fields encapsulation https://en.m.wikipedia.org/wiki/Field_encapsulation No wonder that you don't understand properties if you don't know why getters and setters are useful.
Why would you want that? Wouldn't eliminating inheritance add a lot of boilerplate code?
However, I may just be weird. My Java coding style is probably closer to Rust or Haskell. Lots of interfaces defining my type requirements, and static methods to implement compound operations on those interfaces. I like this because it's easily testable and traceable.
You're stretching a bit too far. Inheritance is nothing but composition with some default implicit virtual method routing. You admit as much in your comment. If you want tools, it's more like a hammer vs a mallet and a prybar. Same tools, same purpose, just one is bundled.
EDIT: To be clear, in inheritance, the contract being implemented is the implicit contract specified by the parent type. (Assuming you're following the Liskov substitution principle, which you should be.)
> Also, maybe eliminate inheritance and force interfaces and composition instead.
Implementing an interface meets all the conditions of an is a relationship; no redefinition is necessary. [0] After all, interfaces are nothing more than abstract base classes with no method bodies to inherit. I haven't argued anywhere that composition defines an is a relationship, so I have no idea where you're pulling that from.
[0] https://en.m.wikipedia.org/wiki/Is-a#Java
EDIT: I really like this article you linked. It has all sorts of things that help me prove my point. For instance, the Rectangle example from the LSP section. If Rectangle were an interface:
interface IRectangle {
double getWidth();
double getHeight();
}
It's trivial to implement concrete Rectangle and Square classes that don't violate any principles. Inheritance, on the other hand, is pretty much guaranteed to be broken as long as Rectangle is mutable, because Square cannot add additional constraints to Rectangle and still pretend to be a Rectangle in all use-cases.Edit: I don't see any composition in your example, just an interface implementation.
EDIT: Notice how I can enforce the invariants of `Square`, while composing a `Rectangle` and implementing `IRectangle`. This works because composition allows me to encapsulate the problematic setter methods of `Rectangle`, which is not something I can do if I inherit it.
class Square implements IRectangle {
private final Rectangle rectangle;
public Square(final double size) {
this.rectangle = new Rectangle(size, size);
}
public double getWidth() {
return this.rectangle.getWidth();
}
public double getHeight() {
return this.rectangle.getHeight();
}
public double getSize() {
return this.rectangle.getWidth();
}
public double setSize(final double size) {
this.rectangle.setWidth(size);
this.rectangle.setHeight(size);
}
}