Na teoria da linguagem de programação, um tipo é um conjunto de valores. Por exemplo, o tipo "int" é o conjunto de todos os valores inteiros.
Nas linguagens OOP, uma classe é um tipo, é?
Quando uma classe é definida com mais de um membro, por exemplo,
class myclass{
int a;
double b;
}
Quando falamos de uma aula, queremos dizer
- "
(a,b)
ondea
é um int eb
é um duplo", ou - "{
(x,y)
|x
é int,y
existe um duplo}"?
O que significa uma instância de myclass
?
- "
(a,b)
ondea
é um int eb
é um duplo", ou - um objeto que ocupa um espaço de memória e que pode (não necessariamente, ou seja, pode estar vazio) armazenar
(x,y)
, ondex
existe int e qualy
é o dobro?
a
eb
são membros desse tipo, como menciona Killian Forth.Myclass é isomórfico para registros com camposa
eb
do tipoint
edouble
- você pode pegar um registro como esse e transformá-lo em uma instância demyclass
.Respostas:
Nem.
Suponho que você esteja se perguntando se ter o mesmo conjunto de tipos de campo é suficiente para classificar como sendo da mesma classe ou se eles precisam ser nomeados de forma idêntica. A resposta é: "Nem mesmo os mesmos tipos e nomes são suficientes!" Classes estruturalmente equivalentes não são necessariamente compatíveis com o tipo.
Por exemplo, se você tem um
CartesianCoordinates
e umaPolarCordinates
classe, eles podem tanto ter dois números como os seus campos, e eles podem até ter o mesmoNumber
tipo e os mesmos nomes, mas eles ainda não seria compatível, e uma instânciaPolarCoordinates
não seria um instância deCartesianCoordinates
. A capacidade de separar tipos pelo objetivo pretendido e não pela implementação atual é uma parte muito útil para escrever código mais seguro e mais sustentável.fonte
interface
em Java e C #, se você quiser fazer testes de unidade. Você acaba escrevendo uma tonelada de clichê para poder alterar a classe específica que seu programa usará, mesmo que você não tenha nenhuma intenção de alterá-lo em tempo de execução.Tipos não são conjuntos.
Veja bem, a teoria dos conjuntos tem vários recursos que simplesmente não se aplicam aos tipos e vice-versa . Por exemplo, um objeto tem um único tipo canônico. Pode ser uma instância de vários tipos diferentes, mas apenas um desses tipos foi usado para instanciar. A teoria dos conjuntos não tem noção de conjuntos "canônicos".
A teoria dos conjuntos permite criar subconjuntos em tempo real , se você tiver uma regra que descreva o que pertence ao subconjunto. A teoria dos tipos geralmente não permite isso. Embora a maioria dos idiomas tenha um
Number
tipo ou algo semelhante, eles não têm umEvenNumber
tipo, nem seria fácil criar um. Quero dizer, é fácil definir o tipo em si, mas quaisquerNumber
s existentes que sejam pares não serão magicamente transformados emEvenNumber
s.Na verdade, dizer que você pode "criar" subconjuntos é um tanto falso, porque conjuntos são um tipo diferente de animal. Na teoria dos conjuntos, esses subconjuntos já existem , de todas as maneiras infinitas que você pode defini-los. Na teoria dos tipos, geralmente esperamos lidar com um número finito (se grande) de tipos a qualquer momento. Os únicos tipos que dizem existir são aqueles que realmente definimos, e não todos os tipos que poderíamos definir.
Os conjuntos não têm permissão para se conter direta ou indiretamente . Algumas linguagens, como Python, fornecem tipos com estruturas menos regulares (no Python,
type
o tipo canônico étype
eobject
é considerado uma instância deobject
). Por outro lado, a maioria dos idiomas não permite que tipos definidos pelo usuário se envolvam nesse tipo de truque.Geralmente, os conjuntos podem se sobrepor sem estar contidos um no outro. Isso é incomum na teoria dos tipos, embora algumas linguagens a suportem na forma de herança múltipla. Outras linguagens, como Java, apenas permitem uma forma restrita disso ou o proíbem totalmente.
O tipo vazio existe (é chamado de tipo inferior ), mas a maioria dos idiomas não o suporta ou não o considera como um tipo de primeira classe. O "tipo que contém todos os outros tipos" também existe (é chamado de tipo superior ) e é amplamente suportado, diferentemente da teoria dos conjuntos.
NB : Como alguns comentadores apontaram anteriormente (antes da discussão do tópico), é possível modelar tipos com teoria dos conjuntos e outras construções matemáticas padrão. Por exemplo, você pode modelar a associação de tipo como uma relação, em vez de modelar tipos como conjuntos. Mas, na prática, isso é muito mais simples se você usar a teoria das categorias em vez da teoria dos conjuntos. É assim que Haskell modela sua teoria de tipos, por exemplo.
A noção de "subtipagem" é realmente bem diferente da noção de "subconjunto". Se
X
for um subtipo deY
, significa que podemos substituir instâncias deY
por instâncias deX
e o programa ainda "funcionará" em algum sentido. Isso é comportamental e não estrutural, embora algumas linguagens (por exemplo, Go, Rust, sem dúvida C) tenham escolhido a última por razões de conveniência, seja para o programador ou para a implementação da linguagem.fonte
Os tipos de dados algébricos são a maneira de discutir isso.
Existem três maneiras fundamentais de combinar tipos:
Produtos. É basicamente o que você está pensando:
é um tipo de produto; seus valores são todas as combinações possíveis (ou seja, tuplas) de um
int
e umdouble
. Se você considerar os tipos de número como conjuntos, a cardinalidade do tipo de produto é de fato o produto das cardinalidades dos campos.Soma. Nas linguagens procedurais, é um pouco estranho expressar diretamente (classicamente, isso é feito com uniões marcadas ); portanto, para melhor entendimento, aqui está um tipo de soma no Haskell:
os valores desse tipo têm o formato
AnInt 345
ouADouble 4.23
, mas sempre há apenas um número envolvido (diferente do tipo de produto, em que cada valor tem dois números). Portanto, a cardinalidade: você primeiro enumera todos osInt
valores, cada um deve ser combinado com oAnInt
construtor. Além disso , todos osDouble
valores, cada um combinado comADouble
. Daí o tipo de soma .Exponenciação 1 . Não discutirei isso em detalhes aqui, porque não há nenhuma correspondência clara de OO.
E as aulas? Eu usei deliberadamente a palavra-chave
struct
e nãoclass
paraIntXDouble
. O fato é que uma classe como um tipo não é realmente caracterizada por seus campos, esses são apenas detalhes de implementação. O fator crucial é, antes, que valores distinguíveis a classe pode ter.O que é relevante, porém, é que o valor de uma classe pode ser o valor de qualquer uma de suas subclasses ! Portanto, uma classe é na verdade um tipo de soma e não um tipo de produto: se
A
eB
seriam derivados demyClass
, omyClass
seria essencialmente a soma deA
eB
. Independentemente da implementação real.1 São funções (no sentido matemático! ); um tipo de função
Int -> Double
é representado pelo exponencialDouble
Int
. Muito ruim se o seu idioma não tem funções adequadas ...fonte
static
métodos, exceto que ainda não são valores de primeira classe. O que acontece com a maioria das linguagens OO é que elas aceitam objetos como o menor bloco de construção; portanto, se você deseja algo menor, precisa falsificá-lo com objetos e ainda acaba arrastando várias funções semânticas. Por exemplo, não faz sentido comparar funções para igualdade, mas você ainda pode comparar dois objetos de função falsa.Desculpe, mas eu não sei sobre a teoria "bruta". Só posso fornecer uma abordagem prática. Espero que isso seja aceitável em programmers.SE; Não estou familiarizado com a etiqueta aqui.
Um tema central da OOP é a ocultação de informações . O que os membros de dados de uma classe são exatamente não devem interessar a seus clientes. Um cliente envia mensagens para (chama métodos / funções-membro de) uma instância, que pode ou não modificar o estado interno. A idéia é que os internos de uma classe possam mudar, sem que o cliente seja afetado por ela.
Um corrolário disso é que a classe é responsável por garantir que sua representação interna permaneça "válida". Vamos assumir uma classe que armazena um número de telefone (simplificado) em dois números inteiros:
Estes são os membros de dados da classe. No entanto, a classe provavelmente será muito mais do que apenas seus membros de dados e certamente não é definível como "conjunto de todos os valores possíveis de int x int". Você não deve ter acesso direto aos membros dos dados.
A construção de uma instância pode negar qualquer número negativo. Talvez a construção também normalize o código de área de alguma forma ou até verifique o número inteiro. Você teria, assim, acabar muito mais perto do seu
"(a,b) where a is an int and b is a double"
, porque certamente não é qualquer dois int armazenado nessa classe.Mas isso realmente não importa no que diz respeito à classe. Não é o tipo dos membros dos dados, nem o intervalo de seus possíveis valores que define a classe; são os métodos que são definidos para ela.
Enquanto esses métodos permanecerem os mesmos, o implementador poderá alterar os tipos de dados para ponto flutuante, BIGNUMs, seqüências de caracteres, o que for e para todos os fins práticos, ainda seria a mesma classe .
Existem padrões de design para garantir que tais alterações na representação interna possam ser feitas sem que o cliente esteja ciente disso (por exemplo, o idioma pimpl em C ++, que oculta qualquer membro de dados atrás de um ponteiro opaco ).
fonte
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.
Os membros dos dados não definem uma classe apenas quando você os oculta. Esse pode ser o caso mais comum, mas certamente não se pode dizer que é verdade para todas as classes. Se um único campo é público, é tão importante quanto seus métodos.class
es. (Marcá-losfinal
ajuda a entender o ponto, mas ainda assim). Você ainda tem um problema com osprotected
membros, que podem ser herdados e, portanto, fazem parte de uma segunda API para implementadores de subclasses.protected
quanto possível na prática ;-).)class
é uma construção dependente da linguagem. Até onde eu sei, não existeclass
teoria teórica.Um tipo é uma descrição de uma categoria / faixa de valores, estruturas compostas ou o que você possui. OOPwise, é semelhante a uma "interface". (No sentido agnóstico da linguagem. O sentido específico da linguagem, nem tanto. Em Java, por exemplo,
int
é um tipo , mas não tem relação com umainterface
. As especificações de campo público / protegido também não fazem parte de uminterface
, mas fazem parte de uma "interface" ou tipo .)O ponto principal é que é muito mais uma definição semântica do que concreta. Estruture apenas fatores na medida em que os campos / comportamentos expostos e seus propósitos definidos estejam alinhados. Se você não possui os dois, não possui compatibilidade de tipo.
Uma classe é a realização de um tipo. É um modelo que realmente define a estrutura interna, o comportamento anexado etc.
fonte