Skip to main content
In Dart, Object is the base class for all non-nullable types. Because Dart is a pure object-oriented language, every value—including primitives like integers, booleans, and functions—is an instance of a class that implicitly inherits from Object. Under Dart’s sound null safety system, the absolute root of the type hierarchy is Object? (nullable Object), making Object the root of all types except Null.

Type Hierarchy and Static Typing

Dart distinguishes between Object, Object?, and dynamic at the compiler level:
  • Object: The top non-nullable type. A variable of type Object can hold any value except null. The compiler enforces strict type checking, allowing access only to methods defined on the Object class itself.
  • Object?: The true top type. It is equivalent to Object | Null.
  • dynamic: A static type annotation that tells the compiler to disable static type checking. While a dynamic variable can hold the same values as Object?, it permits arbitrary method invocations at compile time, deferring type resolution to runtime.
Object nonNullableObj = 42; // Valid
// nonNullableObj = null;   // Compile-time error

Object? nullableObj = null; // Valid

// Type checking enforcement
Object strictObj = "String value";
// strictObj.length;        // Compile-time error: 'length' is not defined on 'Object'

dynamic dynamicObj = "String value";
print(dynamicObj.length);   // Compiles and runs successfully

Core Interface

Because all non-nullable classes inherit from Object, they inherit its core interface. These methods and properties are frequently overridden in custom classes to define specific behaviors for equality, hashing, and string representation.

1. operator ==(Object other)

Evaluates equality. By default, the Object implementation performs an identity check (reference equality), returning true only if both references point to the exact same instance in memory.

2. int get hashCode

Returns an integer representing the object’s hash code. It is a strict rule in Dart that if two objects are equal according to operator ==, they must return the exact same hashCode.

3. String toString()

Returns a string representation of the object. The default implementation returns Instance of 'ClassName'.

4. Type get runtimeType

A property that returns the Type object representing the actual runtime type of the instance. It is generally evaluated at runtime and should not be used for type checking (use the is operator instead).

5. dynamic noSuchMethod(Invocation invocation)

Invoked automatically by the Dart runtime when code attempts to access a method or property that does not exist on the object. The default implementation throws a NoSuchMethodError. It returns dynamic to allow intercepted method calls to return values.

Syntax: Overriding Object Methods

When defining a class, extends Object is implicit. Overriding the inherited Object methods conventionally uses the @override annotation. While highly recommended and enforced by standard lint rules, the @override annotation is not strictly required by the Dart compiler to successfully override a method.
class Entity {
  final int id;
  final String name;

  Entity(this.id, this.name);

  // Overriding == requires the parameter to be of type Object
  @override
  bool operator ==(Object other) {
    return identical(this, other) ||
        (other is Entity && 
         runtimeType == other.runtimeType && 
         id == other.id);
  }

  // hashCode must be overridden if == is overridden.
  // It must only rely on fields used in the operator == check.
  @override
  int get hashCode => id.hashCode;

  @override
  String toString() => 'Entity(id: $id, name: $name)';
}
Tired of Poor Dart Skills? Fix That With Deep Grasping!Learn More