Classes: Inheritance

Hack supports single inheritance between classes.

class IntBox {
  public function __construct(protected int $value) {}

  public function get(): int {
    return $this->value;
  }
}

class MutableIntBox extends IntBox {
  public function set(int $value): void {
    $this->value = $value;
  }
}

Classes can access things defined in the parent class, unless they are private.

Overriding Methods

You can override methods in subclasses by defining a method with the same name.

class IntBox {
  public function __construct(protected int $value) {}

  public function get(): int {
    return $this->value;
  }
}

class IncrementedIntBox extends IntBox {
  <<__Override>>
  public function get(): int {
    return $this->value + 1;
  }
}

If a method is intended to override a method in a parent class, you should annotate it with <<__Override>>. This has no runtime effect, but ensures you get a type error if the parent method is removed.

Hack does not support method overloading. Subclasses methods must have a return type, parameters and visibility that is compatible with the parent class.

class NumBox {
  public function __construct(protected num $value) {}

  protected function get(): num {
    return $this->value;
  }
}

class FloatBox extends NumBox {
  <<__Override>>
  public function get(): float {
    return (float)$this->value;
  }
}

The only exception is constructors, which may have incompatible signatures with the parent class. You can use <<__ConsistentConstruct>> to require subclasses to have compatible types.

class User {
  // This constructor takes one argument.
  public function __construct(protected string $name) {}
}

class Player extends User {
  // But this constructor takes two arguments.
  <<__Override>>
  public function __construct(protected int $score, string $name) {
    parent::__construct($name);
  }
}

Calling Overridden Methods

You can use parent:: to call an overridden method in the parent class.

class IntBox {
  public function __construct(protected int $value) {}

  public function get(): int {
    return $this->value;
  }
}

class IncrementedIntBox extends IntBox {
  <<__Override>>
  public function get(): int {
    return parent::get() + 1;
  }

This also works for static methods.

class MyParent {
  public static function foo(): int {
    return 0;
  }
}

class MyChild extends MyParent {
  <<__Override>>
  public static function foo(): int {
    return parent::foo() + 1;
  }
}

Abstract Classes

An abstract class cannot be instantiated.

abstract class Animal {
  public abstract function greet(): string;
}

class Dog extends Animal {
  <<__Override>>
  public function greet(): string {
    return "woof!";
  }
}

This allows new Dog() but not new Animal().

Abstract classes are similar to interfaces, but they can include implementations of methods.

Final Classes

A final class cannot have subclasses.

final class Dog {
  public function greet(): string {
    return "woof!";
  }
}

If your class has subclasses, but you want to prevent additional subclasses, use <<__Sealed>>.

If you want to inherit from a final class for testing, use <<__MockClass>>.

You can also combine final and abstract on classes. This produces a class that cannot be instantiated or have subclasses. The class is effectively a namespace of grouped functionality.

abstract final class Example {
  public static function callMe(int $i): int {
    return static::helper($i);
  }

  private static function helper(int $i): int {
    return $i + 1;
  }
}

Final Methods

A final method cannot be overridden in subclasses.

class IntBox {
  public function __construct(protected int $value) {}

  final public function get(): int {
    return $this->value;
  }
}