[БЕЗ_ЗВУКА] В этом видео мы рассмотрим такую важную часть языка Go, как интерфейсы. Через интерфейсы в Go реализован полиморфизм, то есть возможность функции принимать в себя аргументы различных типов. В отличие от таких языков, как C++, Java и PHP, где типизация явная, в Go реализована утиная типизация. Что это значит? В C++, когда вы создаете класс, вы явно указываете, что он реализует такой интерфейс. «Вот мои документы, вот я наследовался от этого интерфейса, я его реализую, все хорошо. Я... Я — утка. Я точно знаю, что я — утка». В Go, когда вы создаете структуру с методами, она не знает, какому интерфейсу она соответствует, то есть типизация неявная. Там применяется такой подход: Если это утка, оно крякает как утка, плавает как утка и летает как утка, то это утка. То есть за соблюдение контракта этого интерфейса отвечает не сама структура, а метод, в который вы ее передаете. То есть вы передаете в метод, который принимает определенный интерфейс. А уже интерфейс описывает, что если мы хотим сюда попасть, там должны быть определенные методы. То есть подход не снизу вверх вот так вот, а, наоборот, вот так. То есть вы можете передать в функцию ту структуру, даже если она не знает, что она реализует этот интерфейс. Давайте посмотрим, как это работает в коде. Итак, мы объявляем тип интерфейса, то есть указываем type, указываем имя этого интерфейса и ключевое слово interface, чтобы указать, что этот тип является интерфейсом. В данном случае я объявил интерфейс Payer, то есть «плательщик», то есть для того чтобы соответствовать этому интерфейсу, мне нужно иметь метод pay, «заплатить», который принимает в себя какой-то int и возвращает ошибку. Дальше я реализую структуру «кошелек», у которого есть какое-то поле cash, то есть количество денег в этом кошельке, и есть метод «заплатить». Обратите внимание: в реализации кошелька нигде нет упоминания того, что он каким-либо образом реализует интерфейс «плательщик». Теперь, как это вызывается. Есть функция Buy, которая принимает в себя интерфейс «плательщик». Она не знает уже, какая структура туда придет. Ей просто важно, чтобы то, что придет в эту функцию, обладало методом Pay. Все. То есть реализовывало этот интерфейс. Внутри я уже вызываю у того аргумента, который придал, я вызываю метод Pay, для того чтобы заплатить, списать деньги, и все. Теперь функция main, то есть я создаю кошелек, в котором есть какие-то деньги, и передаю в функцию buy. Я нигде не делаю никаких преобразований, ничего, просто передаю сразу как есть. Давайте посмотрим, как это работает. Отлично! «Спасибо за покупку через main.Wallet», через кошелек. T, «спасибо за покупку через кошелек T», при указании, что мы хотим тут получить тип переданного аргумента. Это был простой пример. У нас была всего одна структура, которая реализовывала интерфейс. Давайте теперь рассмотрим несколько более сложный пример, когда у нас есть несколько структур, которые реализуют интерфейс. У нас есть кошелек, он реализует метод Pay, у него есть какое-то количество денег в нем. У нас есть карточка, структура в которой: баланс, дата, до которой она валидна, CVV, имя карточки, имя владельца, и она тоже реализует метод Pay. И есть ApplePay, когда вы прикладываете телефон для бесконтактной оплаты. У него есть количество денег на аккаунте и какой-то AppleID. И он тоже реализует метод Pay. Ну и так же наш интерфейс «плательщик», который требует, только чтобы был метод Pay. И функция «купить». Теперь я создаю, смотрите, я создаю кошелек, и могу через него что-то купить. Я создаю теперь уже не сразу, не сразу какой-то объект, который я буду дальше передавать, а создаю переменную типа «кошелек», типа «плательщик». И теперь в эту переменную я могу присваивать любые структуры, которые реализуют этот кошелек. То есть я могу не только ее...Эта проверка на интерфейс может быть не только при передаче в функцию, но и при присвоении в переменную. То есть эта переменная может иметь тип interface. Соответственно, вы можете, при создании структуры вы можете указывать, что какое-то поле должно иметь определенный интерфейс, соответствовать определенному интерфейсу. И, наконец, я присваиваю в эту созданную переменную, которая реализует интерфейс «плательщик», я присваиваю уже другую реализацию этого плательщика. Если раньше это была карточка, то теперь это ApplePay. Ну и надо запустить. [ЗВУК] Вот я успешно оплатил через кошелек, через карточку, а на то, чтобы оплатить через ApplePay, денег на балансе, на аккаунте не хватило. Вот так. Но иногда нам бывает нужно не просто вызывать какие-то методы интерфейса, но и проверить, что в действительности нам пришло. Для этих целей у нас есть специальная конструкция. Называется она type switch. Мы можем, используя эту конструкцию, мы можем определить, какой тип к нам пришел, какой тип лежит по интерфейсу. Реализуется она через оператор switch, p и запрос типа в скобочках. И уже в case нам придут типы, которые реализует этот... В case нам придут, нам будут передаваться типы, которые может реализовать этот интерфейс. Смотрите, если у меня интерфейс, если мой плательщик представлен типом «кошелек», я напишу «Оплата наличными». Если мой интерфейс представлен типом Card, я хочу получить доступ к данным этой карточки. Например, проверить имя владельца. Зачем нужна такая конструкция? Когда я передаю интерфейс, эта переменная имеет тип inetrface, и я не могу обратиться к полям структуры, которая лежит под этим интерфейсом. Если я так попробую сделать, будет паника. Давайте я вам ее покажу. [ЗВУК] [ЗВУК] Я сейчас до преобразования интерфейса в оригинальную структуру попробую вывести какое-то имя, вывести имя владельца карточки. Оп, ошибка! Cardholder undefined (type Payer has no field or method Cardholder). Что это значит? Что у моего интерфейса, у него нет такого поля нигде. Потому что это сейчас не структура, это интерфейс. Я могу вызывать только методы этого интерфейса. Теперь мне нужно преобразовать. Итак, я пишу p., входящая моя переменная, и в скобках я указываю тип, к которому я хочу его привести. И мне возвращается уже значение этого типа и признак того, получилось ли его преобразовать, действительно ли он там имеется или нет. В данном случае я мог бы опустить обработку ошибки, потому что я нахожусь под switch case. Но если вы это преобразовываете без switch case, и там действительно может попасться какая-то другая реализация, не та, которую вы хотите, то проверять ошибку, конечно, надо. Теперь в plasticCard у меня действительно лежит мой тип Card, и я могу обратиться к его полям, это уже не интерфейс. Ладно. А теперь, если это не карта, то я вывожу «Что-то новое!». Давайте это запустим. Отлично. Оплата наличными через Wallet. «Вставляйте карту, rvasily», то есть я обратился к методу структуры, к структуре Card. Ну и «Что-то новое!» при ApplePay. Это интерфейсы. Далее мы рассмотрим пустой интерфейс, который позволяет передать в себя вообще все что угодно.