What are the main advantages of a single-root class hierarchy when all classes are inherited from one class, for example Object ? What prompted the developers of languages ​​with a single-root class hierarchy to go along this path?
5 answers
Mainly to ensure that all objects and classes have a common behavior, as well as the default behavior, which is contained in virtual methods that can be overridden. For this, a single root object is best. This ensures that all types will have certain methods.
In order to be able to write members (methods, properties, indexers, events, etc.) that can work in different ways with completely different types. However, this feature was greatly leveled by more convenient Generic and removed the need for type checking and ghosting, errors in which could be seen during execution. However, there is still an API (including the standard one), which was developed before Generic, and which actively uses the root type.
I don’t know about the advantages, but, it seems to me, it’s quite logical sometimes (sometimes because you don’t need to push it everywhere to the place and out of place) to determine the common part that can be inherited by classes. And sometimes it turns out that when analyzing and building a future structure (hierarchy), it turns out that all classes will have some common fields and methods (Naturally, the process of aligning the hierarchy does not take a day, and sometimes not even a month. This must be remembered). So in C # and Java there is a toString method that returns a string representation of the object, Finalize - in which you can describe the logic when the object is destroyed and other fields / methods. There would be no point in describing these methods constantly in dozens of other classes. This is at least redundancy.
This is somehow more natural, in some analogy: there is someone who believes that people are descended from Adam and Eve. And this is a kind of the same hierarchy and inheritance from the root)) This hierarchy is also easier to understand: a tree, only growing from top to bottom, having common roots, feeding from one source, and not as planting shrubs, weeds-breeches, for which no you can see your vegetable garden and your tomatoes with cucumbers.
Multiple inheritance provides additional flexibility and the ability to reuse code, but it raises a number of problems related to ambiguity, which occurs when two or more ancestors of our class have matching methods. The most famous problem is the so-called. “diamond problem” when we inherit simultaneously from two classes (let's call them, for example, A and B), which are descendants of the same common ancestor, and each of our two ancestors in its own way redefines the method defined in general ancestor, say int common(int i) . When code using our class calls its common(33) method, which of the methods - A.common () or B.common () - should be executed?
Different languages ​​in which multiple inheritance is implemented solve problems in different ways, but in any case, these solutions lead to a complication of the language, sometimes artificial limitations, etc.
In real projects, the hierarchies of the classes used are usually quite complex and confusing (just look at the standard class libraries in any normal language), and the problems of multiple inheritance under these conditions can turn into a considerable headache, causing a lot of errors that can be difficult to find and fix.
In addition, multiple inheritance often provokes the programmer to use confusing and ambiguous solutions that contribute to the appearance of errors, in contrast to single inheritance, which, as a rule, provides greater logical clarity and symmetry of decisions, and therefore reduces the likelihood of errors.
Therefore, many programmers (including the creators of Java) came to the conclusion that multiple inheritance in its pure form brings more hemorrhoids and headaches than flexibility, and abandoned it. And the desired flexibility is quite successfully achieved by other means - interfaces, generic classes, etc.
See https://ru.wikipedia.org/wiki/Rombid_investigation , and in general you can search where they write about it - for example. http://www.viva64.com/ru/b/0204/
- 3As far as I understand, the author does not consider the expediency of multiple inheritance. He is interested in the expediency of inheritance (implicit in the main) from a single root type. Even despite the prohibition of multiple inheritance, language developers could not create a single root type from which absolutely all types will inherit. Therefore, your answer is good, but absolutely not suitable for this issue. - Vadim Ovchinnikov
- oneMultiple inheritance has nothing to do with the presence / absence of the
Objectclass (the class from which all other classes are always inherited). Python3 - there is multiple inheritance and a commonObjectclass; Java - no multiple inheritance, there is a commonObject; C ++ has multiple inheritance; there is no genericObject. - Arnial
For example, in Java, "simple" (non-multiple) inheritance is used, then without a single root of the hierarchy it would be difficult (not possible) to organize containers - list, map, etc.
Such an organization of hierarchy was introduced into Smalltalk and many languages ​​took this idea from it.
In my opinion, such an organization is easier to understand and reduces the number of errors in development.
- Please explain the thought "without a single root of the hierarchy, it would be difficult (not possible) to organize containers - list, map, etc." - Vladimir Gamalyan
- @VladimirGamalian take a List, it has an add method that adds a new item to the list. This method has a parameter. The parameter must be of some type. If there is no single root, then it is not possible to indicate the type of this parameter, so that any object could be placed in this list without additional efforts. - Mikhail Vaysman
- oneAnd zheneriki is not it about that? - Vladimir Gamalyan
- @VladimirGamalian and generics are just possible because of the presence of a single root. Read about type erasure. - Mikhail Vaysman
- one@MikhailVaysman type erasure is a feature of the implementation of generics in Java. The same C #, from which Java copied the implementation, completely dispenses with Type erasure. - PashaPash ♦
My imho and no more than that - that this is to some extent a replacement for generalized programming. For example, I personally, even before STL in C ++, terribly wanted to write a container class of the type of the current vector. It was easy to do, for my purposes it was done - but it was necessary to specify a specific type. As a result, there were two options - either for each type to rewrite the code, or to generate all the classes that I would like to hide in the container, from some common (at the same time, the fundamental types appeared in the span). Templates quite gracefully solved this problem.
As far as I know Java, there initially went a different way, and generics - this is an opportunity for a completely different time. The same (correct, if I am mistaken) even the fundamental types are in principle classes. This is another design solution that provided a replacement for generic programming in C ++, and which is actively used in the language itself - for the same transformation, for example, into strings.
Object. And with what to compare these benefits? - Vadim Ovchinnikov