Dans la théorie du langage de programmation, un type est un ensemble de valeurs. Par exemple, le type "int" est l'ensemble de toutes les valeurs entières.
Dans les langages POO, une classe est un type, n'est-ce pas?
Lorsqu'une classe est définie avec plusieurs membres, par exemple
class myclass{
int a;
double b;
}
Quand nous parlons d'une classe, voulons-nous dire
- "
(a,b)
oùa
est un int etb
est un double", ou - "{
(x,y)
|x
est un entier,y
est-ce un double}"?
Que signifie une instance de myclass
?
- "
(a,b)
oùa
est un int etb
est un double", ou - un objet qui occupe un espace mémoire et qui peut (pas nécessairement, c'est-à-dire peut être vide) stocker
(x,y)
, oùx
est tout int ety
tout double?
a
et neb
sont pas membres de ce type, comme le mentionne Killian Forth. Myclass est isomorphe aux enregistrements avec des champsa
etb
de typeint
etdouble
- vous pouvez prendre un enregistrement comme celui-ci et le transformer en une instance demyclass
.Réponses:
Ni.
Je suppose que vous demandez si le fait d'avoir le même ensemble de types de champs suffit à classer comme étant la même classe, ou s'ils doivent également être nommés de manière identique. La réponse est: "Ne pas même avoir les mêmes types et les mêmes noms suffit!" Les classes structurellement équivalentes ne sont pas nécessairement compatibles avec les types.
Par exemple, si vous avez un
CartesianCoordinates
et unePolarCordinates
classe, ils pourraient tous deux avoir deux nombres comme champs, et ils pourraient même avoir le mêmeNumber
type et les mêmes noms, mais ils ne seraient toujours pas compatibles, et une instance dePolarCoordinates
ne serait pas un instance deCartesianCoordinates
. La possibilité de séparer les types en fonction de leur objectif et non de leur implémentation actuelle est un élément très utile pour écrire du code plus sûr et plus facile à gérer.la source
interface
en Java et C # si jamais vous voulez faire des tests unitaires. Vous finissez par écrire une tonne de passe-partout juste pour pouvoir changer la classe particulière que votre programme utilisera même si vous n'avez pas l'intention de la changer au moment de l'exécution.Les types ne sont pas des ensembles.
Vous voyez, la théorie des ensembles a un certain nombre de fonctionnalités qui ne s'appliquent tout simplement pas aux types, et vice-versa . Par exemple, un objet a un seul type canonique. Il peut s'agir d'une instance de plusieurs types différents, mais un seul de ces types a été utilisé pour l'instancier. La théorie des ensembles n'a aucune notion d'ensembles "canoniques".
La théorie des ensembles vous permet de créer des sous-ensembles à la volée , si vous avez une règle qui décrit ce qui appartient au sous-ensemble. La théorie des types ne le permet généralement pas. Alors que la plupart des langues ont un
Number
type ou quelque chose de similaire, elles n'en ont pasEvenNumber
et il ne serait pas simple d'en créer un. Je veux dire, il est assez facile de définir le type lui-même, mais toutNumber
s existant qui se trouve même ne sera pas magiquement transformé enEvenNumber
s.En fait, dire que vous pouvez "créer" des sous-ensembles est quelque peu fallacieux, car les ensembles sont un tout autre type d'animal. Dans la théorie des ensembles, ces sous-ensembles existent déjà , de toutes les façons infinies que vous pouvez les définir. Dans la théorie des types, nous nous attendons généralement à traiter un nombre fini (s'il est grand) de types à un moment donné. Les seuls types qui existeraient sont ceux que nous avons réellement définis, pas tous les types que nous pourrions éventuellement définir.
Les ensembles ne sont pas autorisés à se contenir directement ou indirectement . Certains langages, tels que Python, fournissent des types avec des structures moins régulières (en Python,
type
le type canonique de esttype
etobject
est considéré comme une instance deobject
). D'un autre côté, la plupart des langues ne permettent pas aux types définis par l'utilisateur de s'engager dans ce genre de tromperie.Les ensembles sont généralement autorisés à se chevaucher sans être contenus les uns dans les autres. Ceci est rare dans la théorie des types, bien que certains langages le prennent en charge sous forme d'héritage multiple. D'autres langages, tels que Java, n'autorisent qu'une forme restreinte de cela ou l'interdisent entièrement.
Le type vide existe (il est appelé le type inférieur ), mais la plupart des langues ne le prennent pas en charge ou ne le considèrent pas comme un type de première classe. Le "type qui contient tous les autres types" existe également (il est appelé le type supérieur ) et est largement pris en charge, contrairement à la théorie des ensembles.
NB : Comme certains commentateurs l'ont souligné précédemment (avant que le thread ne soit déplacé vers le chat), il est possible de modéliser des types avec la théorie des ensembles et d'autres constructions mathématiques standard. Par exemple, vous pouvez modéliser l'appartenance à un type sous forme de relation plutôt que modéliser des types sous forme d'ensembles. Mais en pratique, c'est beaucoup plus simple si vous utilisez la théorie des catégories au lieu de la théorie des ensembles. C'est ainsi que Haskell modélise sa théorie des types, par exemple.
La notion de "sous-typage" est vraiment très différente de la notion de "sous-ensemble". Si
X
est un sous-type deY
, cela signifie que nous pouvons remplacer des instances deY
par des instances deX
et le programme "fonctionnera" dans un certain sens. C'est comportemental plutôt que structurel, bien que certains langages (par exemple Go, Rust, sans doute C) aient choisi ce dernier pour des raisons de commodité, soit pour le programmeur soit pour l'implémentation du langage.la source
Les types de données algébriques sont le moyen d'en discuter.
Il existe trois façons fondamentales de combiner des types:
Produit. C'est essentiellement ce à quoi vous pensez:
est un type de produit; ses valeurs sont toutes les combinaisons possibles (c'est-à-dire des tuples) de un
int
et undouble
. Si vous considérez les types de nombres comme des ensembles, la cardinalité du type de produit est en fait le produit des cardinalités des champs.Somme. Dans les langages procéduraux, c'est un peu gênant à exprimer directement (classiquement, cela se fait avec des unions étiquetées ), donc pour une meilleure compréhension, voici un type de somme dans Haskell:
les valeurs de ce type ont soit la forme
AnInt 345
, soitADouble 4.23
, mais il n'y a toujours qu'un seul nombre impliqué (contrairement au type de produit, où chaque valeur a deux nombres). Donc la cardinalité: vous énumérez d'abord toutes lesInt
valeurs, chacune doit être combinée avec leAnInt
constructeur. De plus , toutes lesDouble
valeurs, chacune combinée avecADouble
. D'où le type de somme .Exponentiation 1 . Je ne discuterai pas de cela en détail ici car il n'y a pas du tout de correspondance OO claire.
Et les cours? J'ai délibérément utilisé le mot-clé
struct
plutôt queclass
pourIntXDouble
. Le fait est qu'une classe en tant que type n'est pas vraiment caractérisée par ses champs, ce ne sont que des détails d'implémentation. Le facteur crucial est plutôt de savoir quelles valeurs distinctes la classe peut avoir.Ce qui est pertinent cependant, c'est que la valeur d'une classe peut être des valeurs de n'importe laquelle de ses sous-classes ! Ainsi, une classe est en fait un type de somme plutôt qu'un type de produit: si
A
etB
seraient tous deux dérivés demyClass
, lamyClass
serait essentiellement la somme deA
etB
. Quelle que soit la mise en œuvre réelle.1 Il s'agit de fonctions (au sens mathématique! ); un type de fonction
Int -> Double
est représenté par l'exponentielleDouble
Int
. Dommage si votre langue n'a pas de fonctions appropriées ...la source
static
méthodes, sauf qu'elles ne sont toujours pas des valeurs de première classe. La chose à propos de la plupart des langages OO est qu'ils prennent les objets comme le plus petit bloc de construction, donc si vous voulez quelque chose de plus petit, vous devez le simuler avec des objets, et vous finissez toujours par glisser dans un tas de fonctions sémantiques. Par exemple, cela n'a pas de sens de comparer des fonctions pour l'égalité, mais vous pouvez toujours comparer deux objets de fausses fonctions.Désolé mais je ne connais pas la théorie "brute". Je ne peux que fournir une approche pratique. J'espère que cela est acceptable chez programmers.SE; Je ne connais pas l'étiquette ici.
Un thème central de la POO est la dissimulation d'informations . Ce que sont exactement les données des membres d'une classe ne devrait pas intéresser ses clients. Un client envoie des messages à (appelle des méthodes / fonctions membres de) une instance, qui peut ou non modifier l'état interne. L'idée est que les internes d'une classe peuvent changer, sans que le client en soit affecté.
Un corrolaire à cela est que la classe est responsable de s'assurer que sa représentation interne reste "valide". Supposons une classe qui stocke un numéro de téléphone (simplifié) dans deux entiers:
Ce sont les données membres de la classe. Cependant, la classe sera probablement bien plus que ses membres de données, et elle n'est certainement pas définissable comme "ensemble de toutes les valeurs possibles de int x int". Vous ne devez pas avoir un accès direct aux membres des données.
La construction d'une instance peut refuser tout nombre négatif. Peut-être que la construction normaliserait également le code régional d'une certaine manière, ou même vérifierait le nombre entier. Vous finirez ainsi jusqu'à beaucoup plus proche de votre
"(a,b) where a is an int and b is a double"
, car il est certainement pas tout deux int stocké dans cette classe.Mais cela n'a pas vraiment d'importance en ce qui concerne la classe. Ce n'est ni le type des membres de données, ni la plage de leurs valeurs possibles qui définit la classe, ce sont les méthodes qui sont définies pour elle.
Tant que ces méthodes restent les mêmes, l'implémenteur pourrait changer les types de données en virgule flottante, BIGNUMs, chaînes, peu importe, et à toutes fins pratiques, ce serait toujours la même classe .
Il existe des modèles de conception pour garantir que de tels changements de représentation interne peuvent être effectués sans que le client n'en soit conscient (par exemple, l'idiome pimpl en C ++, qui cache tous les membres de données derrière un pointeur opaque ).
la source
It is neither the type of the data members, nor the range of their possible values that defines the class, it's the methods that are defined for it.
Les membres de données ne définissent pas une classe uniquement lorsque vous les masquez. C'est peut-être le cas le plus courant, mais on ne peut certainement pas dire que c'est vrai pour toutes les classes. Si même un seul champ est public, il est tout aussi important que ses méthodes.class
es. (Les marquerfinal
aide à faire passer le message, mais quand même).protected
Cependant, vous avez toujours un problème avec les membres, qui peuvent être hérités et font donc partie d'une deuxième API pour les implémenteurs de sous-classes.protected
que possible dans la pratique. ;-))class
est une construction dépendante du langage. Pour autant que je sache, laclass
théorie des types n'existe pas .Un type est une description d'une catégorie / plage de valeurs, de structures composées ou de ce que vous avez. OOPwise, il s'apparente à une "interface". (Dans le sens indépendant de la langue. Le sens spécifique à la langue, pas tellement. En Java, par exemple,
int
est un type , mais n'a aucun rapport avec uninterface
. Les spécifications de champ public / protégé, également, ne font pas partie d'uninterface
, mais font partie d'une "interface" ou d'un type .)Le point principal étant, c'est beaucoup plus une définition sémantique que concrète. La structure ne tient compte que dans la mesure où les champs / comportements exposés et leurs objectifs définis s'alignent. Si vous n'avez pas les deux, vous n'avez pas de compatibilité de type.
Une classe est la réalisation d'un type. C'est un modèle qui définit réellement la structure interne, le comportement attaché, etc.
la source