Переходя от C ++ к Java, очевидный вопрос без ответа состоит в том, почему Java не включает перегрузку операторов?
Не Complex a, b, c; a = b + c;
намного ли проще, чем Complex a, b, c; a = b.add(c);
?
Есть известная причина этого веские аргументы для не позволяя перегружать оператор? Причина произвольна или потеряна во времени?
java
operator-overloading
ренголин
источник
источник
Ответы:
Предполагая, что вы хотите перезаписать предыдущее значение объекта, на который ссылается
a
, тогда должна быть вызвана функция-член.В C ++ это выражение говорит компилятору создать три (3) объекта в стеке, выполнить сложение и скопировать результирующее значение из временного объекта в существующий объект
a
.Однако в Java
operator=
не выполняется копирование значений для ссылочных типов, и пользователи могут создавать только новые ссылочные типы, а не типы значений. Таким образом, для определяемого пользователем типа с именемComplex
присваивание означает копирование ссылки на существующее значение.Рассмотрим вместо этого:
В C ++ это копирует значение, поэтому сравнение будет неравным. В Java
operator=
выполняет эталонное копирование, поэтомуa
иb
сейчас ссылаются на одно и то же значение. В результате сравнение будет производить «равный», так как объект будет сравнивать равный самому себе.Разница между копиями и ссылками только добавляет путаницы в перегрузку операторов. Как упоминал @Sebastian, Java и C # оба должны иметь дело со значением и ссылочным равенством по отдельности -
operator+
скорее всего, будут иметь дело со значениями и объектами, ноoperator=
уже реализованы для работы со ссылками.В C ++ вы должны иметь дело только с одним видом сравнения за раз, так что это может быть менее запутанным. К примеру, на
Complex
,operator=
иoperator==
оба работают над значениями - копирование значения и сравнения значений соответственно.источник
Есть много постов с жалобами на перегрузку операторов.
Я чувствовал, что должен прояснить концепции «перегрузки операторов», предлагая альтернативную точку зрения на эту концепцию.
Код запутывает?
Этот аргумент является ошибкой.
Обфускация возможна на всех языках ...
Код в C или Java также легко запутать с помощью функций / методов, как в C ++ с помощью перегрузок операторов:
... даже в стандартных интерфейсах Java
Для другого примера, давайте посмотрим
Cloneable
интерфейс в Java:Вы должны клонировать объект, реализующий этот интерфейс. Но ты мог бы лгать. И создать другой объект. На самом деле, этот интерфейс настолько слаб, что вы можете вернуть другой тип объекта, просто для удовольствия:
Поскольку
Cloneable
интерфейс может быть злоупотреблен / запутан, должен ли он быть запрещен по тем же причинам, что и перегрузка оператора C ++?Мы можем перегрузить
toString()
методMyComplexNumber
класса, чтобы он возвращал строковый час дня. СледуетtoString()
ли запретить перегрузку? Мы могли бы саботировать,MyComplexNumber.equals
чтобы он возвращал случайное значение, изменял операнды ... и т. Д. И т. Д.В Java, как в C ++ или любом другом языке, программист должен соблюдать минимум семантики при написании кода. Это означает реализацию
add
функции, которая добавляет, иCloneable
метод реализации, который клонирует, и++
оператор, который увеличивается.Что в любом случае запутывает?
Теперь, когда мы знаем, что код можно саботировать даже с помощью нетронутых методов Java, мы можем задаться вопросом о реальном использовании перегрузки операторов в C ++?
Понятная и естественная запись: методы против перегрузки операторов?
Ниже мы сравним, для разных случаев, «один и тот же» код в Java и C ++, чтобы понять, какой стиль кодирования более понятен.
Естественные сравнения:
Обратите внимание, что A и B могут быть любого типа в C ++, если предусмотрены перегрузки операторов. В Java, когда A и B не являются примитивами, код может стать очень запутанным, даже для примитивных объектов (BigInteger и т. Д.) ...
Естественные массивы / контейнерные средства доступа и подписка:
В Java мы видим, что для каждого контейнера, выполняющего одно и то же (доступ к его содержимому через индекс или идентификатор), у нас есть другой способ сделать это, что сбивает с толку.
В C ++ каждый контейнер использует один и тот же способ доступа к своему контенту благодаря перегрузке операторов.
Естественные продвинутые типы манипуляций
В приведенных ниже примерах используется
Matrix
объект, найденный с помощью первых ссылок, найденных в Google для « объекта Java Matrix » и « объекта C ++ Matrix »:И это не ограничивается матрицами. В
BigInteger
иBigDecimal
классах Java страдают от того же запутанным многословия, в то время как их эквиваленты в C ++ являются ясно , как встроенными типами.Естественные итераторы:
Природные функторы:
Конкатенация текста:
Хорошо, в Java вы
MyString = "Hello " + 25 + " World" ;
тоже можете использовать ... Но, подождите секунду: это перегрузка операторов, не так ли? Разве это не обман?:-D
Общий код?
Одни и те же операнды, модифицирующие общий код, должны использоваться как для встроенных модулей / примитивов (которые не имеют интерфейсов в Java), так и для стандартных объектов (которые не могут иметь правильный интерфейс), и пользовательских объектов.
Например, вычисление среднего значения двух значений произвольных типов:
Обсуждение перегрузки оператора
Теперь, когда мы увидели справедливые сравнения между кодом C ++, использующим перегрузку операторов, и тем же кодом в Java, теперь мы можем обсудить «перегрузку операторов» как концепцию.
Перегрузка оператора существовала еще до появления компьютеров
Даже за пределами компьютерной науки, есть перегрузка операторов: например, в математике, операторы любят
+
,-
,*
и т.д. перегружены.Действительно, значения
+
,-
,*
и т.д. изменяется в зависимости от типов операндов (Числовые, векторы, квантовое волновые функции, матрицы и т.д.).Большинство из нас, как часть наших научных курсов, изучили множество значений для операторов, в зависимости от типов операндов. Мы нашли их смущающими, их?
Перегрузка оператора зависит от его операндов
Это самая важная часть перегрузки операторов: как и в математике или физике, операция зависит от типов ее операндов.
Итак, знайте тип операнда, и вы будете знать эффект операции.
Даже C и Java имеют (жестко запрограммированную) перегрузку операторов
В С реальное поведение оператора будет меняться в зависимости от его операндов. Например, добавление двух целых чисел отличается от добавления двух двойных или даже одного целого и одного двойного. Существует даже целая арифметическая область указателя (без приведения можно добавить к указателю целое число, но нельзя добавить два указателя ...).
В Java нет арифметики указателей, но кто-то все еще нашел бы, что конкатенация строк без
+
оператора была бы достаточно нелепой, чтобы оправдать исключение в кредо «перегрузка оператора - зло».Просто вы, как программист C (по историческим причинам) или Java (по личным причинам , см. Ниже), не можете предоставить свой собственный.
В C ++ перегрузка операторов не является обязательной ...
В C ++ перегрузка операторов для встроенных типов невозможна (и это хорошо), но пользовательские типы могут иметь пользовательские перегрузки операторов.
Как уже говорилось ранее, в C ++ и, в отличие от Java, пользовательские типы не считаются гражданами второго сорта по сравнению со встроенными типами. Таким образом, если встроенные типы имеют операторы, пользовательские типы также должны иметь их.
Истина заключается в том, что, как и
toString()
,clone()
,equals()
методы для Java ( т.е. квази-стандарт типа ), C ++ перегрузка оператора настолько части C ++ , что он становится столь же естественно , как исходные операторы C, или ранее упомянутыми методов Java.В сочетании с шаблонным программированием перегрузка операторов становится широко известным шаблоном проектирования. Фактически, вы не сможете зайти слишком далеко в STL, не используя перегруженные операторы и перегружая операторы для своего собственного класса.
... но этим нельзя злоупотреблять
Перегрузка оператора должна стремиться соблюдать семантику оператора. Не вычитать в
+
операторе (как в «не вычитать вadd
функции» или «вернуть дерьмо вclone
методе»).Перегрузка броска может быть очень опасной, потому что она может привести к неясностям. Таким образом, они действительно должны быть зарезервированы для четко определенных случаев. Что же касается
&&
и||
, никогда не перегружать их , если вы действительно не знаете , что вы делаете, как вы будете терять оценку на короткое замыкание , что нативные операторы&&
и||
наслаждаться.Итак ... Хорошо ... Тогда почему это невозможно в Java?
Потому что Джеймс Гослинг сказал так:
Пожалуйста, сравните текст Гослинга выше со Страуструпом ниже:
Будет ли перегрузка операторов полезной для Java?
Некоторые объекты могут значительно выиграть от перегрузки операторов (конкретные или числовые типы, такие как BigDecimal, комплексные числа, матрицы, контейнеры, итераторы, компараторы, парсеры и т. Д.).
В C ++ вы можете воспользоваться этим преимуществом благодаря скромности Страуструпа. В Java вы просто облажались из-за личного выбора Гослинга .
Можно ли добавить его в Java?
Причинами отсутствия добавления перегрузки операторов сейчас в Java могут быть сочетание внутренней политики, аллергии на эту функцию, недоверия разработчиков (вы знаете, диверсантов, которые, как кажется, часто посещают команды Java ...), совместимости с предыдущими JVM, время написать правильную спецификацию и т.д ..
Так что не ждите, пока эта функция ...
Но они делают это на C # !!!
Да...
Хотя это далеко не единственное различие между двумя языками, этот не может не удивить меня.
По-видимому, ребята из C # с их «каждым примитивом a
struct
иstruct
производным от Object» сделали это правильно с первой попытки.И они делают это на других языках !!!
Несмотря на все FUD против используемой определенной перегрузки операторов, ее поддерживают следующие языки: Scala , Dart , Python , F # , C # , D , Algol 68 , Smalltalk , Groovy , Perl 6 , C ++, Ruby , Haskell , MATLAB , Eiffel , Lua , Clojure , Fortran 90 , Swift , Ada , Delphi 2005 ...
Так много языков, так много разных (и иногда противоположных) философий, и все же они все согласны с этим.
Пища для размышлений ...
источник
Джеймс Гослинг сравнил проектирование Java со следующим:
Вы можете прочитать контекст цитаты здесь
В основном перегрузка операторов отлично подходит для класса, который моделирует какую-то точку, валюту или комплексное число. Но после этого у вас быстро заканчиваются примеры.
Другим фактором было злоупотребление функцией в C ++ разработчиками, перегружающими такие операторы, как '&&', '||', операторы приведения и, конечно, 'new'. Сложность, возникающая из-за того, что это сочетается с передачей по значению и исключениями, хорошо освещена в книге об исключительном языке C ++ .
источник
Many C++ design decisions have their roots in my dislike for forcing people to do things in some particular way [...] Often, I was tempted to outlaw a feature I personally disliked, I refrained from doing so because I did not think I had the right to force my views on others. (B. Stroustrup)
.I'd like them to go have a look at some C++ code out there that is hideously put together with weird hacks and "exceptional" features of the language
: Плохие программисты будут писать плохой код независимо от языка. Просто попытайтесь эмулировать «передачу по ссылке» для параметров функции в Java, чтобы иметь представление. Я видел код и смеялся так сильно, что было больно. Это тот тип вещей, который Гослинг не использовал, поэтому в Java требовались ужасные взломы, но он существует изначально, при нулевых затратах, как в C #, так и в C ++.Проверьте Boost.Units: текст ссылки
Он обеспечивает анализ измерений с нулевыми накладными расходами посредством перегрузки операторов. Насколько яснее это можно получить?
на самом деле будет выводить «Энергия = 4 Дж», что правильно.
источник
Java-дизайнеры решили, что перегрузка операторов доставляет больше хлопот, чем стоит. Просто как тот.
В языке, где каждая переменная объекта на самом деле является ссылкой, перегрузка оператора создает дополнительную опасность быть нелогичной - по крайней мере, для программиста C ++. Сравните ситуацию с перегрузкой оператора C # == и
Object.Equals
иObject.ReferenceEquals
(или как там это называется).источник
Groovy перегружен оператором и работает в JVM. Если вы не возражаете против падения производительности (которое уменьшается с каждым днем). Это автоматически на основе имен методов. например, «+» вызывает метод «плюс (аргумент)».
источник
where ...
становится.Where(i => ...
). Если бы они сделали то же самое с арифметическими операторами, многие вещи были бы проще и мощнее. У Java есть преимущество с чистого листа, и оно может сделать это правильно (хотя по религиозным причинам, вероятно, никогда не будет).Я думаю, что это, возможно, был осознанный выбор дизайна, чтобы заставить разработчиков создавать функции, имена которых четко отражают их намерения. В C ++ разработчики перегружали бы операторы функциональностью, которая часто не имела бы отношения к общепринятой природе данного оператора, делая почти невозможным определение того, что делает часть кода, не глядя на определение оператора.
источник
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator
Это необоснованное утверждение. Я профессиональный разработчик C ++ с 12 лет, и я редко сталкивался с этой проблемой. На самом деле, большинство ошибок и ошибок дизайна, которые я видел в C ++, были в коде в стиле C (void *
приведения и т. Д.)add
функция действительно может быть неправильно использована (например, умножение или получение мьютекса) ... Злоупотребление, упомянутое пользователем14128, не ограничивается операторами, но есть некоторый патологический страх перед перегрузкой операторов, который, как мне кажется, возник из ранних дней C и C ++, страх, который не изменился прямо в Java, но, к счастью, не вошел в C # ... В конце концов, соблюдая семантику и написание четких функций / операторов - работа разработчика. Не язык.cout << f() || g();
круглые скобки не проясняют , они исправляют. И если операторы сдвига битов не подвергаются насилию, они не будут необходимы. Почемуcout << (5&3) << endl;
лучше чемcout.fmt(5&3)(endl);
? Использование оператора вызова функции в переменной-члене функтора было бы бесконечно лучшим проектом для потоков, чем перепрофилирование побитовых операторов только потому, что глиф выглядит красиво. Но это далеко не единственное, что не так с потоками.Ну, вы действительно можете выстрелить себе в ногу с перегрузкой оператора. Как с указателями люди делают глупые ошибки с ними, и поэтому было решено убрать ножницы.
По крайней мере, я думаю, что в этом причина. В любом случае, я на вашей стороне. :)
источник
Некоторые люди говорят, что перегрузка операторов в Java приведет к запутыванию. Неужели эти люди когда-нибудь останавливались, чтобы посмотреть на какой-нибудь код Java, выполняющий некоторые базовые математические операции, такие как увеличение финансовой стоимости на процент с помощью BigDecimal? .... многословие такого упражнения становится собственной демонстрацией запутывания. По иронии судьбы, добавление перегрузки операторов в Java позволило бы нам создать наш собственный класс Currency, который сделал бы такой математический код элегантным и простым (менее запутанным).
источник
Сказать, что перегрузка оператора приводит к логическим ошибкам типа, что оператор не соответствует логике операции, это все равно, что ничего не сказать. Тот же тип ошибки произойдет, если имя функции не подходит для логики работы - так в чем же решение: отбросьте возможность использования функции !? Это комичный ответ - «Не подходит для логики работы», каждое имя параметра, каждый класс, функция или что-либо может быть логически неуместным. Я думаю, что эта опция должна быть доступна на респектабельном языке программирования, и те, кто считает его небезопасным - эй, нет, они оба говорят, что вы должны его использовать. Давайте возьмем C #. Они свалили указатели, но эй - есть заявление «небезопасный код» - программа на ваш страх и риск.
источник
Технически, существует перегрузка операторов в каждом языке программирования, который может работать с различными типами чисел, например, целыми и действительными числами. Пояснение: термин перегрузка означает, что для одной функции существует просто несколько реализаций. В большинстве языков программирования предусмотрены различные реализации для оператора +, одна для целых чисел, одна для вещественных чисел, это называется перегрузкой операторов.
Теперь многим кажется странным, что в Java есть перегрузка операторов для оператора + для добавления строк вместе, и с математической точки зрения это было бы действительно странно, но с точки зрения разработчика языка программирования нет ничего плохого в добавлении встроенной перегрузки операторов для оператора + для других классов, например, String. Тем не менее, большинство людей согласны с тем, что, как только вы добавите встроенную перегрузку для + для String, то, как правило, хорошей идеей будет предоставить эту функциональность и разработчику.
Совершенно не согласен с ошибкой, что перегрузка оператора запутывает код, так как это оставлено на усмотрение разработчика. Это наивно думать, и, честно говоря, это стареет.
+1 за добавление перегрузки операторов в Java 8.
источник
+
для конкатенации чего-либо string-ish является ИМХО довольно отвратительным, как и перегрузка/
в C и FORTRAN для целого и дробного деления. Во многих версиях Паскаля использование арифметических операторов в любом числовом типе даст результаты, численно эквивалентные приведению к операндамReal
, хотя результаты, которые могут не быть целыми числами, должны быть переданыTrunc
илиRound
до того, как они могут быть присвоены целым числам.Если предположить, что Java является языком реализации, тогда a, b и c будут ссылками на тип Complex с начальными значениями null. Также предполагая, что Complex является неизменным как упомянутый BigInteger и аналогичный неизменяемый BigDecimal , я думаю, что вы имеете в виду следующее, поскольку вы присваиваете ссылку на Complex, возвращаемую при добавлении b и c, а не сравниваете эту ссылку с a.
источник
Иногда было бы неплохо иметь перегрузку операторов, классы друзей и множественное наследование.
Однако я все еще думаю, что это было хорошее решение. Если бы в Java была бы перегрузка операторов, мы никогда не могли бы быть уверены в значениях операторов, не просматривая исходный код. В настоящее время это не обязательно. И я думаю, что ваш пример использования методов вместо перегрузки операторов также вполне читабелен. Если вы хотите сделать вещи более понятными, вы всегда можете добавить комментарий выше волосатых заявлений.
источник
Это не веская причина для запрета, а практическая причина:
Люди не всегда используют это ответственно. Посмотрите на этот пример из библиотеки Python:
Вот объяснение:
источник
Альтернативы нативной поддержке перегрузки операторов Java
Поскольку в Java нет перегрузки операторов, вот несколько альтернатив, на которые вы можете посмотреть:
Если кто-то знает о других, пожалуйста, прокомментируйте, и я добавлю его в этот список.
источник
Хотя язык Java напрямую не поддерживает перегрузку операторов, вы можете использовать плагин компилятора Manifold в любом проекте Java, чтобы включить его. Он поддерживает Java 8 - 13 (текущая версия Java) и полностью поддерживается в IntelliJ IDEA.
источник