Почему Java нужны интерфейсы, а Smalltalk — нет?

#java #interface #smalltalk

#java #интерфейс #smalltalk

Вопрос:

Я некоторое время программировал на Smalltalk, но мне никогда не были нужны интерфейсы для реализации чего-либо. Тогда почему такие языки, как Java, не могут избавиться от интерфейсов? Это только Smalltalk или есть другой язык, которому не нужны интерфейсы?

Комментарии:

1. Кто сказал, что Java нужны интерфейсы? У него, конечно, есть интерфейсы, но вы можете написать Java-код без интерфейса.

2. Не уверен в светской беседе, но Java использует интерфейсы, потому что множественного наследования нет, но u может реализовать множество интерфейсов

3. Scala — это язык на основе jvm, который использует traits вместо интерфейсов. Черты похожи на абстрактные классы, но с некоторыми ограничениями, позволяющими множественное наследование. Все они хороши: scala-lang.org/node/126

4. @user869097: (проблемы статической типизации в сторону) еще один вопрос, который вы могли бы задать: есть ли другой язык, который заставляет вас использовать эквивалент интерфейсов Java? Любой OOA / OOD (включая те, которые используют множественное наследование) может быть смоделирован в Java с использованием только интерфейсов. Тогда на самом деле возможно написать Java-программу, в которой каждый отдельный класс помечен как окончательный и где нет ни одного абстрактного класса (предпочитайте композицию наследованию и все такое). Я не говорю, что вы должны это делать. Я просто говорю, что вы могли бы, и это пища для размышлений 🙂

5. @user988052: Если вам интересно, на этой странице подробно рассказывается о системе типов: bracha.org/nwst.html (Strongtalk использует термин «протокол» для обозначения своих типов на основе сообщений, как это принято в кругах Smalltalky, но это очень близко эквивалентно интерфейсу)

Ответ №1:

Потому что Java статически типизирована, а Smalltalk — нет. Интерфейсы не служат никакой цели, когда вы не объявляете типы, а ваши переменные не будут проверяться на тип. Но в статически типизированном языке, таком как Java, они чрезвычайно удобны, потому что позволяют вам иметь переменную, тип которой определяется методами, реализуемыми объектом, а не его классом. Это приближает вас к динамической типизации, которую Smalltalk имеет изначально, не отказываясь от преимуществ проверки типов.

Комментарии:

1. Как насчет COM / DCOM? Я могу проверить ВО ВРЕМЯ ВЫПОЛНЕНИЯ, поддерживает ли какой-либо объект какой-либо интерфейс и применить его к нему, а затем использовать этот интерфейс. Интерфейс — это просто контракт, ответственность. ИМХО, правда в том, что Smalltalk — это язык старой школы, которому не хватает многих естественных и ожидаемых функций, не потому, что есть какая-то серьезная причина не иметь их, а просто потому, что он недостаточно зрелый.

Ответ №2:

Это проблема полиморфизма: в Java у вас есть статические типы, и поэтому вам нужно знать, на какие сообщения может отвечать ваш объект … в Smalltalk (и других нестатических языках) вам просто нужно реализовать правильные методы, чтобы иметь полиморфизм.

Например:

  • В Java вам нужно реализовать Cloneable, который определяет метод Cloneable.clone для создания объектов cloneable. Тогда компилятор знает, что ваш объект поймет этот метод (в противном случае он выдаст ошибку)
  • В smalltalk вам просто нужно реализовать метод #clone . Компилятор никогда не знает / не заботится о том, какие сообщения понимают ваши объекты, пока они не будут вызваны.

Это также означает, что вы можете иметь полиморфные объекты, не являясь частью одной иерархии… множественное наследование, микширование и другие подходы (черты присутствуют в Pharo) — это просто техника повторного использования, а не ограничение дизайна.

Такой способ работы часто называют «утиным набором текста»… смотрите: http://en.wikipedia.org/wiki/Duck_typing

Комментарии:

1. 1. Почему вы называете это «проблемой» ?! 2. Python разрешено создавать write() , read() и ваш класс похож на файл. Но сегодня сообщество Python поняло, что это плохая идея, и добавило протоколы. Если у меня есть объект с #clone методом, я понятия не имею, является ли это клонируемым объектом или этот «клон» означает что-то другое. Другой пример: если у меня есть write метод, означает ли это, что у меня open тоже есть close методы and ? Единственным интерфейсом является контракт . В противном случае ваш код будет загрязнен множеством проверок во время выполнения (с отражением) существования некоторых методов и проверкой их аргументов / подписей / арности.

Ответ №3:

Как вы думаете, может ли быть полезная роль для «интерфейсов» в Smalltalk?

См. раздел — Добавление динамических интерфейсов в Smalltalk

Ответ №4:

не уверен, что именно вы спрашиваете (или, скорее, на какой вопрос вы больше всего хотите получить ответ), но взгляните на Ruby. Насколько я понимаю, он намного ближе к smalltalk, чем Java.

Если бы я должен был ответить на вопрос о том, зачем java нужны интерфейсы, я думаю, я бы сказал что-то о том, что java является статически типизированным языком и использует эту философию в той степени, в которой java делает то, что делает необходимость интерфейсов. По сути, интерфейсы пытаются дать вам что-то вроде множественного наследования в java без проблем с множественным наследованием, с которыми сталкиваются другие языки (я полагаю, C ).

Ответ №5:

Java не нуждается в интерфейсах. Это то, что компилятор решил поддерживать, а не отбрасывать.

Во время выполнения интерфейсы не могут быть применены ни на одном языке, потому что все динамические объекты являются либо 1. структурами чистого состояния, либо 2. структурами чистого состояния, где первый элемент является указателем на vtable, отображающим либо целые числа в члены (через массив), либо строки в члены (будучи словарем / хэш-картой). Следствием этого является то, что вы всегда можете изменить значения индексов vtable или записей hashmap, или просто изменить указатель vtable на другой адрес vtable, или просто получить незаконный доступ к памяти.

Smalltalk мог бы легко хранить информацию, предоставленную во время компиляции ваших классов, и в некотором смысле это так и есть, именно так intellisense в браузерах smalltalk выдает предложения для участников, но на самом деле это не принесло бы пользы smalltalk.

Существует несколько проблем с синтаксисом smalltalk, которые ограничивают использование интерфейсов.


  1. Smalltalk имеет только один основной тип

Это означает, что он не может просто предупредить вас, если вы попытаетесь поместить квадрат в отверстие круга, там нет квадратов, нет отверстий, все является объектом для компилятора smalltalk.

Компилятор может выбрать тип для вывода переменных, которые были назначены, но smalltalk философски возражает против этого.


  1. Методы Smalltalk всегда принимают один аргумент

Может показаться, что doing myArray at: 1 put: 'hi' имеет два аргумента, но на самом деле вы вызываете javascript-эквивалент myArray[‘at:put:’]([1, ‘hi’]) с myArray, являющимся объектом (~hashmap). таким образом, количество аргументов не может быть проверено без нарушения философии smalltalk.

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


  1. smalltalk предоставляет свой компилятор среде выполнения, в то время как java очень старается скрыть компилятор от среды выполнения.

Когда вы предоставляете свой компилятор во время выполнения (все языки от ассемблера до javascript могут легко предоставлять свой компилятор во время выполнения, немногие делают его частью легкодоступных частей языка, чем более доступным является компилятор во время выполнения, тем более высоким уровнем мы считаем язык), ваш язык становится немного более хрупкая в том смысле, что информация, которую вы используете во время компиляции в одной строке, может больше не быть действительной в другой строке, потому что во время выполнения информация, на которую компилятор полагался, была исправлена, уже не та.

Одним из следствий этого является то, что класс может иметь один интерфейс в одной точке программы, но на полпути к программе пользователь изменил класс на другой интерфейс; если пользователь хочет использовать этот интерфейс во время компиляции (после изменения класса с помощью кода), компилятору необходимобудьте намного умнее, чтобы понять, что класс, который не поддерживал «.Greet()», теперь внезапно делает или больше не делает, или что метод «.Greet()» и метод «.Foo()» поменялись местами.

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


  1. в отличие от C , smalltalk не использует массивы для vtables, вместо этого он использует сопоставления строк с объектами. Это означает, что даже если вы знаете, что метод существует в вызываемом классе, вы не можете оптимизировать его до dispid, чтобы в будущих вызовах этого метода для поиска метода использовалось смещение массива вместо хеширования. Чтобы продемонстрировать это, давайте используем javascript:

Текущие объекты smalltalk ведут себя аналогично этому:

var myIntVtbl = { ‘ ‘: функция(self, obj1) { возвращает {lpVtbl: myIntVtbl, данные: self.data obj1.data}; } }; var myInt1 = {lpVtbl: myIntVtbl, данные: 2}; var myInt2 = {lpVtbl: myIntVtbl, данные: 5}; var myInt3 = myInt1[‘lpVtbl’][‘ ‘] (myInt1, myInt2); var myInt4 = myInt3[‘lpVtbl’][‘ ‘](myInt3, myInt3); var myInt5 = myInt4[‘lpVtbl’][‘ ‘](myInt4, myInt4); console.log(myInt5);

каждый раз, когда мы вызываем , мы должны хэшировать ‘ ‘, чтобы получить элемент из словаря vtable. Java работает аналогично, именно поэтому декомпиляторы могут так легко определять имена методов.

одна из оптимизаций, которую может выполнить компилятор, если он знает интерфейсы, заключается в компиляции строк в dispids, например:

var myIntVtbl = [ функция(self, obj1) { возвращает {lpVtbl: myIntVtbl, данные: self.data obj1.data}; } ];

var myInt1 = {lpVtbl: myIntVtbl, данные: 2}; var myInt2 = {lpVtbl: myIntVtbl, данные: 5}; var myInt3 = myInt1[‘lpVtbl’][0](myInt1, myInt2); var myInt4 = myInt3[‘lpVtbl’][0](myInt3, myInt3); var myInt5 = myInt4[‘lpVtbl’][0](myInt4, myInt4); console.log(myInt5);

насколько я знаю, ни java, ни smalltalk не делают этого для классов, тогда как C , C # (через атрибут comvisible) делают.


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

Аналогично, Java не нужны интерфейсы, она может буквально работать как smalltalk при условии, что вы предоставляете компилятор java для Java, чтобы быть более доступным. Чтобы понять это, вы можете взаимодействовать между java и движком javascript nashorn, который поставляется со всеми текущими наборами Java, и использовать его функцию eval в качестве компилятора времени выполнения. Java может легко избавиться от интерфейсов и использовать отражающий полиморфизм, обрабатывать все как объект, но будет гораздо сложнее разговаривать с объектами, не позволяя индексировать по строке и перегружая оператор индексации для строки для динамического поиска элементов.