"Why does Inheritance have first-class syntax support in an OOP language like Java ("extends") whereas Composition usually needs to be "engineered" using some more complex patterns, e.g. DI?"
I think the main reason is that DI is seen as an orthogonal concept by language designers, so you'd need new two first-class features in a language.
If you do composition without injection (e.g.: by having new ChildObject() in the constructor), you don't really require that many more lines of code compared to inheritance.
class Car {
private Engine engine;
public Car() {
engine = new Engine();
}
public void drive() {
engine.start();
System.out.println("Driving...");
engine.stop();
}
}
Of course it's much less flexible and less testable than composition + injection, but not that inflexible when compared to inheritance. And first-class support for only that would make the feature a bit useless without DI...
I think the main reason is that DI is seen as an orthogonal concept by language designers, so you'd need new two first-class features in a language.
If you do composition without injection (e.g.: by having new ChildObject() in the constructor), you don't really require that many more lines of code compared to inheritance.
Of course it's much less flexible and less testable than composition + injection, but not that inflexible when compared to inheritance. And first-class support for only that would make the feature a bit useless without DI...