10
$\begingroup$

In some languages, all classes have an implicit superclass. For example, Object in Java/Python and Any in Kotlin. All classes that the user defines automatically are subclasses of this superclass. Oftentimes, this class has many methods that are also inherited by all other classes that are useful for the user, such as some sort of toString() or hash().

What are the benefits or drawbacks associated with this approach for the user or language designer (including for the standard library)?

$\endgroup$
1
  • 1
    $\begingroup$ Nitpick: it is the objects (instances) which are "instances of" some "superclass" object type. Classes may have a superclass, but they don't generally instantiate it. In most languages, they don't instantiate anything at all, and indeed aren't themselves objects. In a language like Python, the distinction between the object type and the type type is essential. $\endgroup$ Jul 7, 2023 at 22:49

3 Answers 3

7
$\begingroup$

Drawback: Universal cluttered namespace

With all classes as subclasses of common type with methods then that means all classes will always have these methods, even where they are invalid in some contexts.

This means you have less control at what the methods your objects will have, and will make any integration with other languages or ABI more difficult as result.

Some of these universal methods have very integrated runtime semantics, and will impose very visible costs on your users, if no downright bugs.


Note that I'm not arguing against having top or bottom types, but against having implemented methods on these top/bottom types. null with implemented methods is a strange thing, as for all objects in existence having the same methods.

$\endgroup$
2
  • $\begingroup$ "null with implemented methods is a strange thing" ... ISTR at least one language whose equivalent of null was a standard object that was defined as accepting any method call with an implementation that just throws an exception, which doesn't seem entirely unreasonable. Perhaps this was Smalltalk? $\endgroup$
    – occipita
    Aug 9, 2023 at 16:47
  • $\begingroup$ Asking a reference that identifies an apple if it is should be viewed as encapsulating the same information as a reference that identifies an orange shouldn't be viewed as an error. Instead, it should simply yield the answer "no, it doesn't". $\endgroup$
    – supercat
    Aug 9, 2023 at 20:38
6
$\begingroup$

This 'super' superclass can define methods that may be important or beneficial for all other classes to implement.

These may be commonly used operations. For example, printing to the standard output via a print() function may need the object to define a toString() method to ensure that any type of object can be printed.

Another type of method that could be encouraged in this way is a hash() function, so things like HashMaps can work properly.

$\endgroup$
5
  • 2
    $\begingroup$ Functions like toString() and hash() can also be automatically generated without any Object superclass, for example Haskell does it using a deriving keyword. $\endgroup$ Jul 7, 2023 at 16:31
  • 2
    $\begingroup$ This is one place where Rust's traits really shine IMO; you can implement them onto anything, and even create blanket implementations to implement them on everything, but you can also choose not to implement a trait like Display or Hash if it wouldn't make sense for your class (whereas making hash a method on the superclass of everything takes away your ability to remove that option if it would be meaningless) $\endgroup$ Jul 7, 2023 at 16:45
  • 3
    $\begingroup$ C++ has operator<<(std::ostream&, T) and std::hash<T> for these cases as opt-in functionality, without needing a universal base class, and that seems to work reasonably well. It might be considered an advantage that types for which those aren't useful can cause compile-time diagnostics rather than meaningless results. $\endgroup$ Jul 11, 2023 at 8:08
  • $\begingroup$ I think having hashCode() in Java is a serious misfeature. There should have been an interface, because as things stand it is impossible to know for sure whether an object can be correctly used as a key in a HashMap (I would argue that using object identity is not correct behaviour in most circumstances, and it should be explicitly called out when it is used). $\endgroup$
    – occipita
    Aug 9, 2023 at 16:53
  • $\begingroup$ @occipita: The problem with hashCode and equals is that they can have two different meanings: will two object references encapsulate the same state if no references are exposed to code that might mutate them, and will they encapsulate the same state forever even if references are exposed? Objects which are "attached" to other entities should often use reference equality for both, immutable objects should use value equality for both, and things like arrays should use value equality for the first but reference equality for the second. $\endgroup$
    – supercat
    Aug 9, 2023 at 20:44
6
$\begingroup$

Ignoring Static Types

Static typing is nice. But sometimes it gets in your way. Sometimes you really, genuinely do know nothing about an object, other than that it exists. Maybe you just deserialized it from some framework, or maybe some external third-party API returned it. However you ended up with it, you know nothing about it yet. That's an Object. Your next step might be to attempt some downcasts or try to identify the object's actual, runtime type in some other way. Or maybe you're just the middleman, transmitting some object you know nothing about from a frontend to a backend.

Yes, it's ugly and messy to do things this way. But programming is ugly and messy sometimes. In this sense, you can think of "upcast to Object" as being a marginally safer void*.

$\endgroup$
1
  • 1
    $\begingroup$ "Oftentimes, this class has many methods". Your answer is about top types, but the question is about top types with methods. $\endgroup$ Jul 11, 2023 at 12:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .