A тип - это классификация части данных, сообщающая вам, каковы ее допустимые значения и допустимые операции. (Почти?) Все языки программирования имеют типы, хотя типизирующая дисциплина значительно варьируется от одного языка к другому.
класса является частным видом типа в ООП языках, который определяется с определенным синтаксисом самого языка (в отличие, скажем, так называемым «родные типов», как в Java int
или float
или подобный, который определяется собственно языком). Класс обычно определяется с точки зрения макета памяти и кодирования данных (так называемые переменные-члены) и функции, которые работают на них (так называемые функции-члены или методы).
интерфейс * является спецификацией каких операций типа необходимо реализовать, чтобы рассматриваться как часть данного множества аналогичных типов, но которые не определяет допустимые значения, макеты памяти и т.д.
Это очень, очень, очень краткий обзор, который является своего рода упрощенной «средней формой» подхода к ним нескольких языков. Он игнорирует несколько краевых случаев и такие вещи, как способность C++ делать вещи, которые являются частью между интерфейсом и классом. Он также игнорирует, какие классы находятся в функциональных языках, таких как Haskell, потому что повреждение вашего мозга дальше не является целью здесь.;)
отредактирован, чтобы добавить некоторые примеры
Вот некоторые ява как декларации, чтобы помочь цементировать концепции.
int myVariable1;
Эта переменная — myVariable1
— является родным (или примитивной) типа, состоящий из 32-разрядного целого числа, закодированного в 2с-комплемента нотации. Он имеет известный диапазон (от примерно -2 млрд до +2 млрд.) И известный набор операций (умножение, добавление, деление, модуль, вычитание, различные преобразования и т. Д.), Доступные ему.
class MyClass
{
int myMemberVariable;
int myOtherMemberVariable;
int myMethod(int p) { myMemberVariable += p; myOtherMemberVariable = p; }
}
MyClass myVariable2 = new MyClass();
Здесь myVariable2
тип определяется классаMyClass
. MyClass
определяет макет памяти (который в этом случае состоит из двух 32-разрядных целых чисел со знаком в примечании 2s-дополнения), а также единственной операции myMethod()
, которая добавляет свой аргумент к myMemberVariable
и устанавливает myOtherMemberVariable
этому аргументу.
interface MyInterface
{
int myInterfaceMethod(int p, int q);
}
Здесь MyInterface
только объявляет набор операций (состоящих в этом случае единственной функцией myInterfaceMethod()
) без каких-либо переменных-членов, и без какой-либо реализации. Он только сообщает вам, что любой класс, реализующий этот интерфейс, гарантированно имеет метод с этой конкретной сигнатурой имени + возвращаемое значение + аргументы. Чтобы использовать его, вы должны создать класс, который реализует интерфейс.
class MyOtherClass implements MyInterface
{
int myMember1;
int myMember2;
int myMember3;
int myInterfaceMethod(int p, int q) { myMember1 = p; myMember2 = q; myMember3 = p - q; }
int myNonInterfaceMethod() { return myMember1; }
}
MyOtherClass myVariable3 = new MyOtherClass();
Теперь myVariable3
определяется как типа с планировкой памяти, состоящей из трех подписаны 32-битных целых и две операции. Одна из этих операций - одна, она должна реализовать из-за целого implements MyInterface
часть. Таким образом, все, что ожидает (абстрактного) типа MyInterface
, может использовать тип (бетон) MyOtherClass
, так как операция гарантирована. Другой метод — myNonInterfaceMethod()
— не не принадлежит к MyInterface, поэтому что-то, что ожидает только MyInterface
, не может его использовать, потому что он не может знать, что он существует.
далееотредактирован, чтобы добавить некоторые реальные вещи по запросу
Если вы когда-либо использовали целое значение, значение с плавающей запятой, строку или что-нибудь подобное в программе вы использовали типы. Типы, возможно, являются материалом для вычислений, и все, что мы делаем, это манипулирование значениями данных типов. Поэтому я сосредоточу внимание на специфических для ООП понятиях классов и интерфейсов.
Каждый раз, когда у вас есть данные и операции с этими данными, у вас есть потенциал для класса. Возьмем, к примеру, банковский счет. У банковского счета будут, среди прочего, номер счета, текущий баланс, лимит транзакции и т. Д.Класс, представляющий это (плохо и показаны только для объяснения понятий) может выглядеть следующим образом:
class BankAccount
{
String accountNumber;
float balance; /* DO NOT USE FLOATING POINT IN REAL FINANCIAL CODE! */
int transaction_limit;
float transaction(float change) {
balance += change > transaction_limit ? transaction_limit : change;
return balance;
}
}
Теперь вы можете сделать переменную этого типа и знать, что он будет носить номер счета (который сам по себе является String
тип), баланс (который сам по себе является float
, но НЕ ИСПОЛЬЗУЙТЕ ПЛАВУЮ ТОЧКУ В РЕАЛЬНОМ МИРОВОМ ФИНАНСОВОМ КОДЕЛЕ!) и лимит транзакции (который сам по себе является int
). Вы также знаете, что можете совершить транзакцию (творчески называется transaction
), которая проверит изменение по лимиту транзакции и изменит баланс. (Настоящий класс для этого будет содержать много больше и будет содержать много предметов обфускационной защиты, которые я удалил для педагогических целей.)
Теперь предположим, что вы находитесь в более сложной финансовой среде, в которой есть несколько виды транзакций, а не только банковские счета. Допустим, у вас есть код, который будет обрабатывать транзакции, которые не волнует, каковы особенности базовых типов:. Форума пакетный процессор транзакций, скажем, что охватывает банковские счета, дебиторская задолженность счетов и т.д. Вместо того, чтобы это знать о всякого рода сделки в книге, мы можем сделать это вместо:
interface Transactable
{
float transaction(float change);
}
class BankAccount implements Transactable
{
/* interior is identical */
}
class ReceivablesAccount implements Transactable
{
float balance;
float transaction(float change) { balance += change; }
}
Теперь все, что знает о типах Transactable
, которые могут использовать как ваши BankAccount
экземпляры, так и ваши экземпляры ReceivablesAccount
. Им не нужно знать, что банковские счета имеют лимиты транзакций, а счета дебиторской задолженности - нет. Им не нужно ничего знать о внутреннем представлении данных. Им не нужно знать особые случаи чего-либо. Они просто должны знать об одной функции по имени (transaction()
) и все. (Если вы хотите более конкретное использование в реальном мире этого, посмотрите, как классы коллекции, не говоря уже о цикле «for in», используют интерфейс Iterable
в Java.)
Концепции четко объясняются. Можете ли вы рассказать о реальном примере жизни, где он используется? –
О боже. Вы ясно объяснили это. Вы очень хороший учитель. Благодарю. –