Inheritance and subtyping

If class X extends (inherits from) class Y, you can use a value with type X (the subtype) whenever a value of type Y (the supertype) is expected. For example, io::File inherits from io::Stream. Therefore, the following code is valid:

var s as Stream
var f = File('data.txt') as File
s = f

As s has type Stream, we can only access members defined in Stream by using s. For example, seek is defined in File but not in Stream:

f.seek(2)    -- Ok
s.seek(2)    -- Error: member "seek" not defined in Stream

The Object type

The Object is the top of the Alore class hierarchy: every other class is a subclass of Object, either directly or via a superclass. Thus any Alore value can be stored in a variable with the Object type:

var o as Object
o = 1               -- Ok
o = 'x'             -- Ok

However, values with the Object type only support a few interesting operations. You can use casts, discussed later in Section Casts, to convert an Object value to a more specific type.

Overriding methods

Overriding with identical signature

If a method overrides a superclass method, the signature of the method must be compatible with the superclass method. In the simple case the signature is identical:

import io

class Super
  def open(path as Str) as Stream
    return File(path)
  end
end

class Sub is Super
  def open(path as Str) as Stream   -- Ok: identical signature
    return File(path + '.ext')
  end
end

Additional arguments in overriding method

The overriding method may define additional optional arguments compared to the original method signature. Intuitively, the overriding method is still compatible because it can be called with either the original signature or with the additional arguments. Here is an example:

class Super
  def f(a as Int)
    ...
  end

  def g() as void
    ...
  end
end

class Sub is Super
  def foo(a as Int, b = '' as Str)  -- Ok, since b is optional
    ...
  end

  def g(*args as Int)   -- Ok, since args is a varargs argument
    ...
  end
end

Contravariance and covariance

In addition, the argument types in the subclass may be supertypes of the corresponding supertype method argument (contravariance). Similarly, the return type may be a subtype of the return type of the supertype method (covariance):

class Sub is Super
  def open(path as Object) as File   -- Ok: contravariant argument type,
    ...                              --     covariant return value type
  end
end

The above example is valid, since Object (type of path) is a supertype of Str, and the return type File is a subtype of Stream.

Overriding and dynamic

We will discuss how the type dynamic affects overriding later in Section The dynamic type and mixed typing.

Overriding member variables and accessors

A subclass may override member variables and accessors defined in a superclass using accessors. In this case the type in the subclass must be equivalent to the superclass type. This example illustrates it:

class Super
  var i as Int
  var o as Object
end

class Sub is Super
  def i as Int    -- Ok, same type as in superclass
    ...
  end

  def o as Str    -- Error: Str incompatible with Object
    ...
  end
end