Допустим, у меня есть этот интерфейс:
public interface IBox
{
public void setSize(int size);
public int getSize();
public int getArea();
//...and so on
}
И у меня есть класс, который реализует это:
public class Rectangle implements IBox
{
private int size;
//Methods here
}
Если бы я хотел использовать интерфейс IBox, я бы не смог создать его экземпляр, например:
public static void main(String args[])
{
Ibox myBox=new Ibox();
}
право? Так что я бы на самом деле должен был сделать это:
public static void main(String args[])
{
Rectangle myBox=new Rectangle();
}
Если это правда, то единственная цель интерфейсов - убедиться, что класс, реализующий интерфейс, содержит в себе правильные методы, описанные интерфейсом? Или есть какое-то другое использование интерфейсов?
java
oop
language-features
interface
Нажмите Upvote
источник
источник
Ответы:
Интерфейсы - это способ сделать ваш код более гибким. Что вы делаете, это:
Затем, позже, если вы решите, что хотите использовать другой тип блока (возможно, есть другая библиотека, с лучшим типом блока), вы переключаете свой код на:
Как только вы привыкнете к этому, вы обнаружите, что это отличный (действительно необходимый) способ работы.
Другой причиной является, например, если вы хотите создать список блоков и выполнить некоторые операции с каждым из них, но вы хотите, чтобы список содержал блоки разных типов. На каждой коробке вы можете сделать:
(при условии, что IBox имеет метод close ()), хотя фактический класс myBox изменяется в зависимости от того, в каком блоке вы находитесь в итерации.
источник
Что делает интерфейсы полезными, так это не тот факт, что «вы можете передумать и позже использовать другую реализацию, и вам нужно только изменить одно место, где создается объект». Это не проблема.
Суть уже в названии: они определяют интерфейс, который любой может реализовать, чтобы использовать весь код, работающий с этим интерфейсом. Лучший пример,
java.util.Collections
который предоставляет все виды полезных методов, которые работают исключительно на интерфейсах, таких какsort()
илиreverse()
дляList
. Дело в том, что этот код теперь можно использовать для сортировки или реверсирования любого класса, который реализуетList
интерфейсы - не толькоArrayList
иLinkedList
, но также и классы, которые вы пишете сами, которые могут быть реализованы так, как люди, которые писали,java.util.Collections
никогда не представляли.Таким же образом вы можете написать код, который работает с хорошо известными интерфейсами или интерфейсами, которые вы определяете, и другие люди могут использовать ваш код, не обращаясь к вам с просьбой поддержать их классы.
Другое распространенное использование интерфейсов для обратных вызовов. Например, java.swing.table.TableCellRenderer , который позволяет вам влиять на то, как таблица Swing отображает данные в определенном столбце. Вы реализуете этот интерфейс, передаете экземпляр в
JTable
, и в какой-то момент во время рендеринга таблицы ваш код будет вызван для выполнения своих задач.источник
you can write code that operates on well-known interfaces, or interfaces you define
Одно из многих применений, которые я прочитал, это где трудно без интерфейсов с множественным наследованием в Java:
Теперь представьте себе случай, когда:
но,
Лучший дизайн будет:
Animal не имеет метода chew () и вместо этого помещается в интерфейс как:
и пусть класс Reptile реализует это, а не Birds (поскольку Birds не может жевать):
а в случае с птицами просто
источник
Reptile
«жует», то не «жует». Соглашение (иногда) об именовании интерфейсов Whateverable должно применяться только там, где это имеет смысл. Наименование интерфейсаPredator
будет более уместным здесь.Назначение интерфейсов - полиморфизм , или замена типа . Например, дан следующий метод:
При вызове
scale
метода вы можете указать любое значение типа, реализующегоIBox
интерфейс. Другими словами, еслиRectangle
и то иSquare
другое реализуетсяIBox
, вы можете предоставить либо a,Rectangle
либоSquare
везде, гдеIBox
ожидается.источник
Интерфейсы позволяют статически типизированным языкам поддерживать полиморфизм. Объектно-ориентированный пурист будет настаивать на том, что язык должен обеспечивать наследование, инкапсуляцию, модульность и полиморфизм для того, чтобы стать полнофункциональным объектно-ориентированным языком. В динамически типизированных - или утиных - языках (таких как Smalltalk) полиморфизм тривиален; однако в статически типизированных языках (таких как Java или C #) полиморфизм далек от тривиальности (на самом деле, на первый взгляд он противоречит понятию строгой типизации).
Позвольте мне продемонстрировать:
В языке с динамической типизацией (или типизированной утиной) (например, Smalltalk) все переменные являются ссылками на объекты (не меньше и не более). Итак, в Smalltalk я могу сделать это:
Этот код:
makeNoise
свинье.Тот же код Java будет выглядеть примерно так (при условии, что Duck и Cow являются подклассами Animal:
Это все хорошо, пока мы не представим класс Овощной. Овощи имеют то же поведение, что и животные, но не все. Например, и Животное, и Овощ могут быть в состоянии расти, но овощи явно не шумят, и животных нельзя добывать.
В Smalltalk мы можем написать это:
Это прекрасно работает в Smalltalk, потому что он имеет тип утка (если он ходит как утка, и крякает как утка - это утка). В этом случае, когда сообщение отправляется объекту, поиск выполняется на список методов получателя, и если соответствующий метод найден, он вызывается. Если нет, выдается какое-то исключение NoSuchMethodError - но все это делается во время выполнения.
Но в Java, статически типизированном языке, какой тип мы можем назначить нашей переменной? Кукуруза должна наследоваться от Овощей, чтобы поддерживать рост, но не может наследоваться от Животных, потому что она не производит шума. Корова должна наследовать от Animal для поддержки makeNoise, но не может наследовать от Vegetable, потому что она не должна реализовывать урожай. Похоже, нам нужно множественное наследование - способность наследовать от нескольких классов. Но это оказывается довольно трудной особенностью языка из-за всех всплывающих случаев (что происходит, когда несколько параллельных суперклассов реализуют один и тот же метод? И т. Д.)
Вдоль интерфейсов ...
Если мы создадим классы Animal и Vegetable, каждый из которых реализует Growable, мы можем объявить, что наша Cow - это Animal, а наша Corn - это Vegetable. Мы также можем заявить, что как Животное, так и Овощное растение. Это позволяет нам написать это, чтобы вырастить все:
И это позволяет нам делать звуки животных:
Преимущество языка типа «утка» в том, что вы получаете действительно хороший полиморфизм: все, что нужно сделать классу для обеспечения поведения, - это предоставить метод. Пока все играют хорошо и только отправляют сообщения, которые соответствуют определенным методам, все хорошо. Недостатком является то, что вид ошибки ниже не обнаруживается до времени выполнения:
Языки со статической типизацией обеспечивают гораздо лучшее «программирование по контракту», потому что во время компиляции они улавливают два вида ошибок:
-
Итак .... подведем итог
Реализация интерфейса позволяет вам определять, что могут делать объекты (взаимодействие), а наследование классов позволяет вам определять, как все должно быть сделано (реализация).
Интерфейсы дают нам много преимуществ «истинного» полиморфизма, не жертвуя проверкой типа компилятора.
источник
Обычно интерфейсы определяют интерфейс, который вы должны использовать (как следует из названия ;-)). Образец
Теперь ваша функция
foo
принимаетArrayList
s,LinkedList
s, ... не только один тип.Самое важное в Java - это то, что вы можете реализовать несколько интерфейсов, но вы можете расширить только ОДИН класс! Образец:
возможно но не является!Код выше может быть:
IBox myBox = new Rectangle();
. Теперь важно то, что myBox содержит ТОЛЬКО методы / поля из IBox, а не (возможно существующие) другие методы изRectangle
.источник
Я думаю, вы понимаете все, что делают интерфейсы, но вы еще не представляете, в каких ситуациях интерфейс полезен.
Если вы создаете экземпляр, используете и выпускаете объект в узкой области (например, в рамках одного вызова метода), интерфейс на самом деле ничего не добавляет. Как вы заметили, конкретный класс известен.
Интерфейсы полезны, когда объект необходимо создать в одном месте и вернуть вызывающей стороне, которая может не заботиться о деталях реализации. Давайте изменим ваш пример IBox на Shape. Теперь у нас могут быть реализации Shape, такие как Rectangle, Circle, Triangle и т. Д. Реализации методов getArea () и getSize () будут совершенно разными для каждого конкретного класса.
Теперь вы можете использовать фабрику с различными методами createShape (params), которые будут возвращать соответствующий Shape в зависимости от переданных параметров. Очевидно, фабрика будет знать о том, какой тип Shape создается, но вызывающая сторона не будет иметь заботиться о том, круг это, или квадрат, или так далее.
Теперь представьте, что у вас есть множество операций, которые вы должны выполнять над вашими фигурами. Может быть, вам нужно отсортировать их по области, установить для них новый размер, а затем отобразить их в пользовательском интерфейсе. Все фигуры создаются фабрикой и затем могут быть легко переданы в классы Sorter, Sizer и Display. Если вам нужно добавить класс шестиугольника в будущем, вам не нужно ничего менять, кроме фабрики. Без интерфейса добавление другой фигуры становится очень грязным процессом.
источник
ты мог бы сделать
Таким образом, вы используете этот объект в качестве Ibox, и вам все равно, что это на самом деле
Rectangle
.источник
Square
вас возникнет проблема .... если вы попытаетесь сделать это без интерфейсов, вы не сможете гарантировать этоSquare
иRectangle
использовать те же методы ... это может привести к кошмару, когда у вас большая база кода ... Помните, что интерфейсы определяют шаблон.ПОЧЕМУ ИНТЕРФЕЙС ??????
Начинается с собаки. В частности, мопс .
Мопс имеет различные поведения:
И у вас есть лабрадор, у которого также есть набор поведения.
Мы можем сделать несколько мопсов и лабораторий:
И мы можем ссылаться на их поведение:
Допустим, я управляю питомником собак и мне нужно отслеживать всех собак, которых я держу. Мне нужно хранить своих мопсов и лабрадоров в отдельных массивах :
Но это явно не оптимально. Если я тоже хочу разместить несколько пуделей , я должен изменить свое определение питомника, чтобы добавить массив пуделей. На самом деле, мне нужен отдельный массив для каждого вида собак.
Понимание: и мопсы, и лабрадоры (и пудели) являются типами собак, и у них одинаковый набор поведений. То есть мы можем сказать (для целей этого примера), что все собаки могут лаять, иметь имя и могут иметь или не иметь курчавый хвост. Мы можем использовать интерфейс для определения того, что могут делать все собаки, но оставим это на усмотрение конкретных типов собак для реализации этих специфических поведений. Интерфейс говорит: «Вот то, что могут делать все собаки», но не говорит о том, как выполняется каждое поведение.
Затем я немного изменил классы Pug и Lab, чтобы реализовать поведение Dog. Можно сказать, что мопс - это собака, а лаборатория - это собака.
Я все еще могу создавать экземпляры Pugs и Labs, как раньше, но теперь у меня есть новый способ сделать это:
Это говорит о том, что d1 - это не только собака, это мопс. И d2 тоже собака, а точнее лаборатория. Мы можем вызвать поведение, и оно работает как прежде:
Вот где вся дополнительная работа окупается. Класс питомника стал намного проще. Мне нужен только один массив и один метод addDog. Оба будут работать с любым объектом, который является собакой; то есть объекты, которые реализуют интерфейс Dog.
Вот как это использовать:
Последнее утверждение будет отображаться: Spot Fido
Интерфейс дает вам возможность указать набор поведений, которые будут общими для всех классов, которые реализуют интерфейс. Следовательно, мы можем определить переменные и коллекции (например, массивы), которым не нужно заранее знать, какой тип конкретного объекта они будут содержать, только что они будут содержать объекты, реализующие интерфейс.
источник
Отличный пример того, как используются интерфейсы, находится в среде Collections. Если вы пишете функцию, которая принимает a
List
, тогда не имеет значения, передаст ли пользователь aVector
или aArrayList
или aHashList
или что-то еще. И вы можете передать этоList
любой функции, для которой требуется интерфейсCollection
илиIterable
.Это делает функции
Collections.sort(List list)
максимально возможными, независимо от того, как ониList
реализованы.источник
По этой причине фабричные шаблоны и другие шаблоны создания так популярны в Java. Вы правы, что без них Java не предоставляет готовый механизм для простой абстракции реализации. Тем не менее, вы получаете абстракцию везде, где вы не создаете объект в вашем методе, который должен составлять большую часть вашего кода.
Кроме того, я обычно призываю людей не следовать механизму «IRealname» для именования интерфейсов. Это вещь для Windows / COM, которая ставит одну ногу в могилу венгерской нотации и на самом деле не нужна (Java уже строго типизирована, и весь смысл в наличии интерфейсов состоит в том, чтобы сделать их как можно более неотличимыми от типов классов).
источник
Не забывайте, что позже вы можете взять существующий класс и реализовать его
IBox
, и тогда он станет доступен для всего вашего кода, поддерживающего коробку.Это становится немного понятнее, если интерфейсы названы -able . например
и т. д. (схемы именования не всегда работают, например, я не уверен, что
Boxable
здесь уместно)источник
Я обновляю ответ новыми функциями интерфейса, которые были представлены в версии Java 8 .
Со страницы документации оракула на резюме интерфейса :
Объявление интерфейса может содержать
Единственными методами, которые имеют реализации, являются стандартные и статические методы.
Использование интерфейса :
Serializable
интерфейс, могут иметь или не иметь какое-либо отношение между ними, кроме реализации этого интерфейсаНекоторые связанные вопросы SE относительно различий между абстрактным классом и интерфейсом и варианты использования с рабочими примерами:
В чем разница между интерфейсом и абстрактным классом?
Как мне объяснить разницу между интерфейсом и абстрактным классом?
Взгляните на страницу документации, чтобы понять новые функции, добавленные в Java 8: стандартные методы и статические методы .
источник
Назначение интерфейсов - абстракция или отделение от реализации.
Если вы вводите абстракцию в своей программе, вас не волнуют возможные реализации. Вы заинтересованы в том, что он может делать, а не как , и вы используете его
interface
для выражения в Java.источник
Если у вас есть CardboardBox и HtmlBox (оба из которых реализуют IBox), вы можете передать оба из них любому методу, который принимает IBox. Даже если они очень разные и не полностью взаимозаменяемы, методы, которые не заботятся об «открытии» или «изменении размера», могут по-прежнему использовать ваши классы (возможно, потому, что им нужно количество пикселей, необходимое для отображения чего-либо на экране).
источник
Интерфейсы, где в Java добавлен объект, позволяющий множественное наследование. Разработчики Java, тем не менее, поняли, что множественное наследование является «опасной» функцией, поэтому пришла в голову идея интерфейса.
множественное наследование опасно, потому что у вас может быть класс, подобный следующему:
Какой будет метод, который должен быть вызван, когда мы используем
Все проблемы решаются с помощью интерфейсов, потому что вы знаете, что можете расширять интерфейсы и что у них не будет методов классификации ... конечно, компилятор хорош и говорит вам, если вы не реализовали методы, но мне нравится думать, что это побочный эффект более интересной идеи.
источник
Вот мое понимание преимуществ интерфейса. Поправь меня, если я ошибаюсь. Представьте, что мы разрабатываем ОС, а другая команда разрабатывает драйверы для некоторых устройств. Поэтому мы разработали интерфейс StorageDevice. У нас есть две реализации (FDD и HDD), предоставленные другими разработчиками.
Затем у нас есть класс OperatingSystem, который может вызывать методы интерфейса, такие как saveData, просто передавая экземпляр класса, реализованный интерфейс StorageDevice.
Преимущество здесь в том, что мы не заботимся о реализации интерфейса. Другая команда выполнит эту работу, реализовав интерфейс StorageDevice.
источник