In informatica, e in particolare in programmazione, si definisce sottotipo un tipo di dato legato ad un altro tipo di dato, detto super-tipo, da una relazione di sostituibilità, intesa nel senso che un programma scritto per utilizzare elementi costruiti sul modello del supertipo, può funzionare anche con elementi costruiti sul modello del sottotipo. In generale, si assume che la relazione supertipo-sottotipo sia quella definita dal principio di sostituzione di Liskov.
Ad esempio, un certo linguaggio potrebbe ammettere l'uso di valori interi al posto di valori floating point, oppure potrebbe definire un tipo numero
in luogo del quale possono essere sia interi sia numeri con virgola. Nel primo caso il tipo intero sarebbe un sottotipo di floating point; nel secondo questi due tipi non avrebbero alcuna mutua relazione reciproca, ma entrambi sarebbero sottotipi di numero
.
La nozione di sottotipo non va confusa con quella di sottoclasse. Nei linguaggi orientati agli oggetti è possibile utilizzare un oggetto di tipo B
dovunque il programma sia scritto per utilizzare un oggetto di tipo A
, purché B
sia una sottoclasse di A
; tuttavia, può essere possibile realizzare la classe B
in modo da violare il "contratto" definito dall'interfaccia di A
, e in questo caso il tipo B non risulterebbe essere un sottotipo del tipo A.
D'altra parte, in alcuni linguaggi è possibile avere sottotipi senza che ci sia ereditarietà tra i tipi coinvolti.
I programmatori traggono vantaggio dall'uso dei sottotipi, in quanto possono scrivere codice in modo più astratto. Ad esempio:
function max (x as numero, y as numero) is if x < y then return y else return x end
se intero e floating point sono entrambi sottotipi di numero
, allora valori di entrambi questi tipi possono essere indifferentemente passati come parametri a questa funzione.
La notazione solitamente usata in teoria dei tipi per indicare la relazione fra tipo e sottotipo è <:
sicché A<:
B significa che A è un sottotipo di B.
Dal punto di vista teorico esiste una fondamentale differenza fra la sottotipizzazione nominale, in cui soltanto i tipi dichiarati secondo determinate regole possono essere uno il sottotipo dell'altro, e la sottotipizzazione strutturale, in cui la struttura stessa di due tipi implica che uno sia il sottotipo dell'altro. La sottotipizzazione sopradescritta si basa sulla nozione di classe, ed è di tipo nominale. Un modo di definire la sottotipizzazione strutturale nel contesto di un linguaggio orientato agli oggetti potrebbe essere affermare che se un oggetto di tipo A è in grado di rispondere a tutti i messaggi a cui è in grado di rispondere un oggetto di tipo B (o, detto in altro modo equivalente, i due oggetti possiedono gli stessi metodi), allora A è un sottotipo di B, indipendentemente dal fatto che i due oggetti siano legati da una relazione di ereditarietà reciproca.
L'implementazione di linguaggi di programmazione con sottotipi cade in due classi: implementazioni inclusive. dove ogni valore di tipo A è anche rappresentazione dello stesso valore di tipo B se A<:
B, e implementazioni coercitive, dove ogni valore di tipo A può essere automaticamente convertito in uno di tipo B.
La sottotipizzazione indotta da una classe (subclassing) in un linguaggio orientato agli oggetti è, solitamente, inclusiva. relazioni di sottotipo tra interi e floating point, che sono rappresentati in modi differenti, sono solitamente coercitive.
In quasi tutti i sistemi che definiscono una relazione di sottotipo, questa è riflessiva (ovvero A<:
A per ogni tipo A) e transitiva (ovvero se A<:
B e B<:
C allora A<:
C). Viene definito in questo modo un preordinamento sui tipi.