Granted, you could have a lot of copy/paste code and NOT use inheritance, but then why use OO in the first place?
EDIT: I mentioned in another comment, and a commenter to this correctly noted- it's probably because it's easier to understand, and a fundamental piece of OO. It's actual usefulness in the real world varies, but it is also how one would step into understanding interfaces, composition, and other aspects of OO that get used heavily in OO in the real world.
type Doer interface {
DoIt()
}
type Foo struct {}
type Bar struct { Foo }
func (*Foo) DoIt () {
fmt.Println("Just do it!")
}
func Incite (d Doer) {
d.DoIt()
}
func main () {
b := &Bar{}
Incite(b)
}
One can transparently invoke Foo's DoIt on instances of Bar or, if Bar had its own DoIt defined, it would override that.Foot-gun wise, of course, if you convert a Bar to a Foo and pass it to something, you no longer get Bar's version of DoIt.
However, anywhere that accepts a Doer accepts a Bar without decomposing it into a Foo, so Incite will invoke Bar's DoIt and, if Bar didn't define a DoIt method, it would still implement Doer via composition of Foo.
One might argue that this is composition not inheritance, but although it's implemented as composition internally, from the outside it appears that Bar isa Doer, even though it doesn't implement DoIt.
Usually, composition would require either forwarding methods which disguise the fact that composition is happening, or for external code to actually access the components directly. Go allows something that looks very much like inheritance.
(I'd also note that, in reality, inheritance is internally implemented as composition. What Go makes explicit is what implicitly happens in C++ or Java anyway.)
>There's an important way in which embedding differs from subclassing. When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one. In our example, when the Read method of a bufio.ReadWriter is invoked, it has exactly the same effect as the forwarding method written out above; the receiver is the reader field of the ReadWriter, not the ReadWriter itself.
Fundamentally, invoking a (non-virtual) superclass's method in C++ doesn't do much more than forward that method to an inner struct.
struct Reader {
int fd;
}
struct Writer {
int fd;
void Write (int) {
// *this* points to the Writer, not the ReadWriter
}
}
int writeForMe (Writer *w) {
// *w* points to the Writer, not the ReadWriter
}
struct ReadWriter: Reader, Writer {
}
The only real difference, aside from the syntax, is that in C++ one can dynamically convert a pointer to a Writer that is part of a ReadWriter into a pointer to that ReadWriter, something Go doesn't support.