Значение типов, класса, интерфейса?

Найти многих авторов взаимозаменяемо используют термин Тип и Класс. В некоторых учебниках, посвященных объектно-ориентированной модели, также используется термин "интерфейс".

Может кто-нибудь объяснить это простыми терминами, основанными на объектно-ориентированном программировании в целом и C++/Java/ объектно-ориентированной базе данных в частности.

4 ответа

Решение

Тип - это классификация фрагмента данных, сообщающая вам, каковы его допустимые значения и допустимые операции. (Почти?) Все языки программирования имеют типы, хотя дисциплина набора значительно варьируется от одного языка к другому.

Класс - это особый тип типа в языках ООП, который определяется с помощью специального синтаксиса в самом языке (в отличие, скажем, от так называемых "нативных типов", таких как Java). int или же float или тому подобное, которые определяются самим языком). Класс обычно определяется с точки зрения структуры памяти и кодирования данных (так называемые переменные - члены) и функций, которые над ними работают (так называемые функции - члены или методы).

Интерфейс * - это спецификация того, какие операции должен выполнять тип, чтобы считаться частью данного набора похожих типов, но в котором не указываются допустимые значения, схемы памяти и т. Д.

Это очень, очень, очень краткий обзор, который является своего рода упрощенной "средней формой" подхода нескольких языков к ним. Он игнорирует некоторые крайние случаи и такие вещи, как способность C++ создавать вещи, которые являются промежуточным звеном между интерфейсом и классом. Он также игнорирует классы на функциональных языках, таких как Haskell, потому что дальнейшее повреждение мозга здесь не является целью.;)


отредактировано, чтобы добавить несколько примеров

Вот некоторые Java- подобные объявления, которые помогут закрепить концепции.

int myVariable1;

Эта переменная myVariable1 - это собственный (или примитивный) тип, состоящий из 32-разрядного целого числа со знаком, закодированного в нотации с дополнением 2s. Он имеет известный диапазон (приблизительно от -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()) без каких-либо переменных-членов и без какой-либо реализации. Это только говорит вам, что любой класс, который реализует этот интерфейс, гарантированно имеет метод с этой конкретной сигнатурой name + return value + arguments. Чтобы использовать его, вы должны создать класс, который реализует интерфейс.

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, используют Iterable интерфейс на Java.)

На языке ООП очень часто используются термины "Тип" и "Класс" взаимозаменяемо, особенно когда речь идет о некоторых языках, таких как Java и / или C++.

Вообще говоря, когда вы определяете класс, вы на самом деле определяете шаблон типа, из которого будут создаваться и / или создаваться объекты определяемого вами типа.

Концепция интерфейса немного сложнее, когда вы определяете интерфейс, вы в основном указываете конкретный способ работы с любым типом объекта, который его использует. То есть ваш объект должен быть из класса, который реализует этот интерфейс. Интерфейс - это способ улучшить ваш класс / тип, указав дополнительное поведение, которое не содержится в исходной спецификации класса / типа.

Для многих практических целей вы можете обращаться с Object полностью, как с его интерфейсом, как если бы он имел тип интерфейса, поэтому, вероятно, термин "интерфейс" используется взаимозаменяемо с термином "тип".

См. Теорию позади абстрактных типов данных для дальнейшего понимания.

Лично я думаю, что это легче понять, если описать это в конкретных терминах. Например, скажем, мы хотим создать программу для отслеживания инвентаря вещей на кухне.

Большинство языков знают только об очень простых, примитивных "типах" таких вещей, как целые числа, символы и байты. Мы могли бы полностью использовать эти примитивные типы для описания типов вещей на кухне. Например, вы можете создать массив из массива символов, где каждый индекс в массиве представляет атрибут чего-либо. Например, индекс 0 представляет "тип" того, что вы пытаетесь описать. Но, конечно, эта техника очень быстро усложняется.

Таким образом, используя объектно-ориентированные методы, мы можем легко определить новые типы, комбинируя примитивные типы. Вы делаете это путем создания определения класса. Думайте о Классе как о проекте для нового Типа.

В примере с кухней у нас есть понятие "Тип питания", "Тип устройства" и "Тип мебели". Чтобы использовать эти новые типы в программе, нам нужно создать определение класса для каждого из них:

public class Food {...}
public class Appliance {...}
public class Furniture {...}

Интерфейс описывает, как мы можем взаимодействовать с чем-то. Например, мы хотим создать инвентарь вещей на нашей кухне. Мы хотим иметь возможность "Добавить" в наш инвентарь, "Удалить" вещи из нашего инвентаря и, возможно, "Перебрать" наш инвентарь. Действия "Добавить", "Удалить" и "Итерация" применяются практически к любому списку чего-либо (не только к нашему инвентарю). Java предоставляет интерфейс с именем "Список" для этой цели. Создав объект инвентаризации с очень популярным и распространенным списком "Интерфейс", мы получаем все действия бесплатно, а другие программисты могут посмотреть на нашу программу и точно знать, как взаимодействовать с объектом "Инвентарь":

...
java.util.List inventory = new ArrayList(); 
...
//later in our program, we can interact with inventory
//  with any of methods on the list interface (because ArrayList "implements" List)
inventory.add(new Furniture("chair"));
inventory.add(new Appliance("refrigerator"));
inventory.add(new Food("ice cream"));
...

Итак, в приведенном выше примере инвентаризация - это переменная, которая содержит экземпляр типа ArrayList. Поскольку определение класса ArrayList реализует интерфейс "Список", все экземпляры типа ArrayList должны подчиняться контракту, определенному в интерфейсе "Список". Другими словами, мы могли бы так же легко создать inventory как LinkedList (вместо ArrayList), например:

java.util.List inventory = new LinkedList();

Не забывайте думать об интерфейсе как о контракте, который предоставляет определенный набор действий, которые должны быть выполнены заранее. Поскольку LinkedList и ArrayList реализуют интерфейс "Список", они оба по контракту обязаны реализовать все методы в интерфейсе "Список".

Самое замечательное в этом то, что любой код, который заинтересован в использовании нашей переменной "инвентаризации", не дает дерьма о том, как реализованы действия "добавить", "удалить" или "итератор". Все, что их волнует, - это то, что переменная "инвентарь" подчиняется интерфейсу "Список".

Другими словами, LinkedList реализует метод add, немного отличающийся от ArrayList. Но это не имеет значения для любого кода, который хочет использовать нашу переменную "инвентарь".

Если какой-нибудь математик придумает революционный способ хранения списков. Возможно, они выясняют, как хранить списки на луне, а не в памяти компьютера, и это оказывается в 10 раз быстрее, чем ArrayList. Тогда все, что нам нужно сделать, это:

java.util.List inventory = new CrazyMoonList();

Весь остальной код, использующий инвентарь, может остаться таким же, потому что CrazyMoonList гарантированно предоставит "добавить", "удалить" и т. Д.

Надеюсь, это поможет уточнить концепции!

Класс - это шаблон для объекта, определяемый пользователем тип данных, который содержит переменные, свойства и методы.

//defines a simple class describing all types of fruit
public class Fruit {
    //insert fields here...

    Fruit(){//constructor method
        //insert constructor code here... 
    }

    //insert class methods here.... 
}

Когда мы создаем экземпляр этого класса как объекта, мы должны сообщить компилятору, какой тип объекта он будет. В этом случае наш объект будет иметь тип Fruit.

//The initialisation might look something like this. 
Fruit myFruit = new Fruit(); 

Предыдущая строка кода указывает компилятору создать новый объект типа Fruit.

Таким образом, в простых терминах "класс" определяет характеристики объекта (его данные), где "тип" относится к тому типу объекта, на который мы смотрим, когда он был создан, будь то Fruit, Car или Desk!

Эта концепция может быть расширена путем дальнейшего изучения тем Наследования и Полиморфизма.

Я надеюсь, что это поможет.

Другие вопросы по тегам