При переходе от C++ к Java очевидный вопрос без ответа: почему в Java не включена перегрузка операторов?
Разве Complex a, b, c; a = b + c; не намного проще Complex a, b, c; a = b.add(c);?
Есть ли для этого известная причина, допустимые аргументы для нет, разрешающие перегрузку оператора? Причина произвольна или потеряна во времени?
@zzzz, мне тяжело читать эту статью. Это был автоматический перевод, или английский - второй язык автора? Я считаю, что обсуждение здесь намного чище.
Для кучи людей, закрывающих это как неконструктивное, этот вопрос привел к одному из самых конструктивных диалогов, которые я видел на SO. Возможно, это лучший кандидат на programmers.stackexchange.com, но бывают моменты, когда я думаю, что SO слишком пренебрежительно относится к более широким темам.
@NoNaMe это просто, просто мысленно вставьте а и в - отсутствующие artlcles - это мертвая распродажа, что человек либо не является носителем английского языка, либо программист (или, как этот парень, оба :) Причина, по которой программисты могут бросать статьи, заключается в том, что это может сделайте комментарии короче и проще в отведенном для этого месте ... оттуда они просто привыкнут. Моя проблема в макете, почему-то я всегда попадаю на этот сайт в поисках Google. К счастью, есть отличное расширение Chrome под названием Четко, которое прекрасно переформатирует трудные для чтения страницы.
Я не вижу причин, почему и как OP принял первый ответ? Ответ @ stackoverflow.com/users/14089/paercebal отличный. Это должно быть принято.
Мне хотелось, чтобы оператор == был перегружен для строк. Получает меня каждый раз, когда я переключаюсь с GO, PHP, RUBY, Python, ....




Ну, вы действительно можете прострелить себе ногу с перегрузкой оператора. Это как с указателями, с ними делают глупые ошибки, поэтому ножницы решили убрать.
По крайней мере, я думаю, что причина в этом. Я все равно на твоей стороне. :)
Как например эта глупая ошибка ...
Это очень плохой образ мышления. Можешь прострелить себе ногу, мы лучше руки порежем, так что у тебя не получится. И, конечно, мы предполагаем, что вы идиот, который застрелится.
Я думаю, что это могло быть сознательным выбором дизайна, чтобы заставить разработчиков создавать функции, имена которых ясно выражают их намерения. В 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 *, приведение типов и т. д.)
-1. Каждая присваиваемая вами переменная является символом, как и символы арифметических операторов. Независимо от того, используете ли вы фразу для обозначения этой переменной, одно слово или одну букву - это ваше (или вашей команды) решение. Кто должен сказать, что имеет смысл, а что нет? Ответ - вы, программист. В чистой математике умножение матриц означает нечто иное, чем умножение двух чисел в базовой арифметике. Тем не менее, мы используем одни и те же символы для обоих типов умножения.
@paercebal: К сожалению, утверждение верно. Чтобы увидеть это в действии, не нужно смотреть дальше, чем IOstreams. К счастью, большинство разработчиков более осмотрительно относятся к созданию новой семантики для существующих операторов.
@BenVoigt: Я не согласен. Использование operator << в iostream хорошо известно уже 30 лет, и визуально его легче понять (например, output << my_value ;), чем его аналог на C ('my_int << my_other_int;', который я даже не могу описать, что именно он делает, не получив моего K&R). Я считаю, что ответ неверен не потому, что он неверен, а потому, что он подразумевает, что таким образом могут быть неправильно использованы только операторы. По сути, пользователь говорит, что мы можем кодировать функции add, которые не связаны с добавлением. Но Vector.add в Java явно использует add для вставки. Следует ли также запретить add? [...]
@BenVoigt: [...] И я даже не упоминаю тот факт, что функция add действительно может быть неправильно использована (например, умножение или получение мьютекса) ... Злоупотребления, упомянутые пользователем 14128, не ограничиваются операторами, но есть какой-то патологический страх перед перегрузкой операторов, который, как я полагаю, исходит из ранних дней C vs. C++, страх, который остался неизменным прямо в Java, но, к счастью, не вошел в C# ... В конце концов, уважая семантика и написание понятных функций / операторов - это работа разработчика. Не языка.
@parcebal: не зная, что << - это оператор сдвига бит без побочных эффектов, вы доказали свою точку зрения. И это не «аналог C», это тоже значение C++. Использование << для вставки потока терпит неудачу, потому что оно имеет приоритет смещения битов, что явно неверно для вставки. Это вызывает гораздо больше ошибок, чем плавный синтаксис вызова функций для потоков.
@BenVoigt Если << воспринимается как неправильный прецедент, тогда код обычно выдает ошибку времени компиляции (например, cout << 5&3 << endl не компилируется). Я должен отметить, что не так много операторов, которые могли бы вызвать этот сбой в C++ (например, побитовые, реляционные, логические, присваивающие, тернарные), и любой сбойный код стал бы довольно сумасшедшим. Я думаю, что в таких случаях 5&3 в любом случае должен быть написан как (5&3), чтобы еще больше уточнить, что это один из элементов для вывода, и что процедуры ввода-вывода Java являются свидетельством того, насколько необходима перегрузка оператора для получения чистого, краткого, читаемого кода.
@ jbo5112: Пример: cout << f() || g(); Скобки не делают его яснее, они делают его правильным. И если бы операторы битового сдвига не использовались, в них не было бы необходимости. Почему cout << (5&3) << endl; лучше чем cout.fmt(5&3)(endl);? Использование оператора вызова функции для переменной-члена функтора было бы бесконечно лучшим дизайном для потоков, чем изменение назначения побитовых операторов только потому, что глиф выглядит красиво. Но это далеко не единственное, что не так со стримами.
Ответ правильный: некоторые разработчики C++ злоупотребляют этой функцией; это аргумент против включения работы этих разработчиков в Стандарт, а не аргумент против наличия этой функции. (Именно здесь Java ошибалась, но она всегда была больше «заставляла разработчиков делать правильные вещи» по сравнению с акцентом C++ на полном контроле)
@BenVoigt cout << f() || g(); - это то же самое, что cout << f(); cout || g();. Если вы не перегрузили класс ostream, чтобы использовать || оператор с типом возвращаемого значения g (), то он не будет компилироваться. Причина, по которой cout << (5&3) << endl; лучше, чем cout.fmt(5&3)(endl);, заключается в том, что первый последовательно использует одну и ту же функцию, а второй вызывает функцию fmt (которая многословна), а затем operator () (например, my_object (std :: endl)).
@BenVoigt Если бы C++ использовал operator () вместо operator << (), тогда это работало бы лучше за счет красивого внешнего вида. Вызов cout("hello ")(name); может показаться некрасивым и медленным для набора, особенно если объединить 350 из них, чтобы создать одну строку вывода (например, длинную запись csv). Использование оператора () может быть лучше cout,"hello ",name;, но cout,c1,',',c2,',',c3,',',c4... немного запутан.
@ jbo5112: Думаю, вы упускаете из виду преобразование iostream-> bool.
Решение @BenVoigt Java по-прежнему не заставляет разработчиков создавать функции, имена которых четко отражают их намерения. Вы все еще можете написать функцию public int add(int num1, int num2) {return ((num1*num2/3)^0xFAFAFAFA);}.
@ jbo5112: Не думаю, что вы понимаете мою позицию. Любым языком можно злоупотреблять; это не делает его плохим. Можно злоупотреблять перегрузкой операторов C++; iostreams показывает, что да (точно так же, как вы продолжаете спорить). Это не означает, что отсутствие перегрузки оператора было бы лучшим выбором. Я цитирую себя: «К счастью, большинство разработчиков более осмотрительно относятся к созданию новой семантики для существующих операторов». Но я с нетерпением жду стандартизованной замены iostreams, что ужасно во многих отношениях, чем просто злоупотребление операторами.
@BenVoigt Вы правы. Я упустил из виду автоматический бросок. Я стараюсь избегать большей части iostream, просто потому, что он медленный для моих целей. Если возврат g () будет приведен к типу bool, это будет выполнено, но я надеюсь, что g () - это хотя бы код для обработки неудачной операции ostream. Извините за то, что вы неправильно интерпретировали ваше отвращение к iostream, перегружающему операторы битового сдвига, как согласие с выбором дизайна Java. Я все еще пытаюсь понять, как Джеймс Гослинг был настолько глуп, чтобы заставить создавать какие-то глупые библиотечные интерфейсы (например, любую математическую библиотеку Java), а люди по-прежнему охотно выбирают язык.
Просто увидеть природу того, что здесь происходит, включая разъяснения и уточнения, достаточно, чтобы заставить меня убежать от перегрузки оператора так быстро, как я могу.
Разработчики Java решили, что перегрузка операторов - больше проблем, чем пользы. Просто как тот.
В языке, где каждая объектная переменная на самом деле является ссылкой, перегрузка оператора создает дополнительную опасность того, что будет совершенно нелогичной - по крайней мере, для программиста на C++. Сравните ситуацию с перегрузкой оператора == в C# и Object.Equals и Object.ReferenceEquals (или как там это называется).
Если предположить, что язык реализации - Java, тогда a, b и c будут ссылками на тип Complex с начальными значениями null. Также предполагая, что Complex является неизменным, как упомянутый BigInteger и аналогичный неизменный BigDecimal, я думаю, вы имеете в виду следующее, поскольку вы назначаете ссылку на Complex, возвращенную при добавлении b и c, а не сравниваете эту ссылку с a.
Isn't :
Complex a, b, c; a = b + c;much simpler than:
Complex a, b, c; a = b.add(c);
Я? ;) Равенство может означать как присваивание, так и сравнение, но = всегда присваивается, а == всегда является сравнением. Имена сами по себе могут стать источником серьезных ошибок.
Groovy имеет перегрузку оператора и работает в JVM. Если вы не возражаете против снижения производительности (который с каждым днем становится все меньше). Это автоматически на основе имен методов. например, "+" вызывает метод "плюс (аргумент)".
Я бы хотел, чтобы все синтаксические языки с перегрузкой операторов использовали эту технику. Я никогда не понимал, зачем им изобретать особую версию именования и поиска методов. Страуструп не упоминает никаких альтернатив в D & EC++. Команда C# выбрала правильный подход с синтаксисом Linq (where ... становится .Where(i => ... ). Если бы они сделали то же самое с арифметическими операторами, многие вещи были бы проще и мощнее. У Java есть преимущество «чистого листа», и она может сделать это правильно (хотя по религиозным причинам этого, вероятно, никогда не будет).
@DanielEarwicker, я часто отмечал, что при возникновении сложных разногласий люди помечают мотивы любой стороны как «религиозные» по своей природе.
@noah, я мог бы жить с ограниченным подмножеством перегрузки операторов, подобным этому, при условии, что в именах методов есть специальный тег, который сохраняет их визуально различимыми. Что-то вроде определения метода __plus () для реализации "+" OL и держаться подальше, перегружая такие вещи, как приведение типов и даже индексы массивов. То, с чем я не хочу жить, - это то, как C++ и C# сочли целесообразным реализовать это.
Предполагая, что вы хотите перезаписать предыдущее значение объекта, на который ссылается a, тогда должна быть вызвана функция-член.
Complex a, b, c;
// ...
a = b.add(c);
В C++ это выражение указывает компилятору создать три (3) объекта в стеке, выполнить сложение и копировать - результирующее значение временного объекта в существующий объект a.
Однако в Java operator= не выполняет копирование значений для ссылочных типов, и пользователи могут создавать только новые ссылочные типы, но не типы значений. Итак, для определяемого пользователем типа с именем Complex присвоение означает копирование ссылки на существующее значение.
Вместо этого подумайте:
b.set(1, 0); // initialize to real number '1'
a = b;
b.set(2, 0);
assert( !a.equals(b) ); // this assertion will fail
В C++ это копирует значение, поэтому результат сравнения не равен. В Java operator= выполняет эталонное копирование, поэтому a и b теперь ссылаются на одно и то же значение. В результате при сравнении будет получено значение «равно», поскольку объект будет сравниваться как равный сам себе.
Разница между копиями и ссылками только усугубляет путаницу, связанную с перегрузкой операторов. Как упоминалось в @Sebastian, Java и C# должны иметь дело с равенством значений и ссылок отдельно - operator+, вероятно, будет иметь дело со значениями и объектами, но operator= уже реализован для работы со ссылками.
В C++ вы должны иметь дело только с одним видом сравнения за раз, чтобы это могло быть менее запутанным. Например, на Complex, operator= и operator== работают со значениями - копируя значения и сравнивая значения соответственно.
К вашему сведению, я не задавал этот вопрос, я только редактировал теги. Щелкните время (сейчас написано «час назад») над моим именем, чтобы получить дополнительную информацию. Похоже, что Ренголин (stackoverflow.com/users/14067/rengolin) задал вопрос.
На самом деле это довольно просто ... Просто делайте то же самое, что и Python, и не делайте перегруженных назначений.
Этот ответ вообще не отвечает на вопрос. Вы просто говорите о том, что Java использует знак равенства. Если бы b + C вернул новый комплекс, тогда a = b + c было бы совершенно корректно, и да, намного проще для чтения. Даже если вы хотите изменить a на месте, a.set (b + c) на тонну проще читать, особенно когда арифметика более чем тривиальна: a.set ((aб + бc) / 5) или a = a. умножить (b). добавить (b. умножить (c)). разделить (5). Твой выбор..
Или я думаю ... не ваш выбор, в зависимости от обстоятельств
В C++ шаблоны выражений решают проблему лишней копии. Практически все основные арифметические библиотеки используют эту технику именно по этой причине. Кроме того, это не решает вопрос, поскольку a = b + c - это просто синтаксический сахар для a.foo (b.bar (c)), что действительно является первоначальным наблюдением в вопросе.
Это не ответ на заданный вопрос. Это чьи-то предположения об определенных различиях между Java и C++.
Посмотрите на проект Java-OO - это модульное расширение (плагин) к компиляторам Java github.com/amelentev/java-oo
Я думаю, что оператор присваивания java всегда делает то, что должен делать. Присвойте значение a значению b. В случае, если они являются ссылками / указателями, очевидно, что мы назначаем указатель, а не то, на что указывает; точно так же, как то, что делает C++. Путаница в том, что в java нет специального синтаксиса для указателей. В случае точки опер. Это работает так же, как оператор -> в C++ для указателей / ссылок java. Это потому, что оператор like :: scoped применяется к классу / перечислению / интерфейсу / и т. д.
Язык с эталонной семантикой не исключает перегрузки операторов. Взгляните, например, на C# или Python.
Джеймс Гослинг сравнил разработку Java со следующим:
"There's this principle about moving, when you move from one apartment to another apartment. An interesting experiment is to pack up your apartment and put everything in boxes, then move into the next apartment and not unpack anything until you need it. So you're making your first meal, and you're pulling something out of a box. Then after a month or so you've used that to pretty much figure out what things in your life you actually need, and then you take the rest of the stuff -- forget how much you like it or how cool it is -- and you just throw it away. It's amazing how that simplifies your life, and you can use that principle in all kinds of design issues: not do things just because they're cool or just because they're interesting."
Вы можете прочитать контекст цитаты здесь
По сути, перегрузка оператора отлично подходит для класса, который моделирует какую-то точку, валюту или комплексное число. Но после этого у вас быстро заканчиваются примеры.
Другим фактором было злоупотребление функцией C++ разработчиками, перегрузившими операторы, такие как '&&', '||', операторы приведения и, конечно же, 'new'. Сложность, возникающая в результате объединения этого с передачей по значению и исключениями, подробно описана в книге Исключительный C++.
Не могли бы вы привести пример кода «сложности перегрузки оператора в сочетании с передачей по значению и исключениями»? Несмотря на несколько лет игры с языком, владение и чтение всех эффективных / исключительных книг по C++, я не понимаю, что вы имеете в виду.
То, что работает для Джеймса Гослинга, не сработает для всех. Он невероятно близорук в том, что экстраполирует свой "интересный" эксперимент с упаковкой, означающий: "Выбросьте в мире все, что мне не нужно, чтобы никто не мог использовать эти вещи". Он явно не знает, что мне нужно и что я использую.
@BT: Больше всего порадовала точка зрения Гослинга по сравнению с точкой зрения Страуструпа по этому вопросу: 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).
@Paercebal: «Больше всего поучительнее Гослинга ... по сравнению с Страуструпом». Интересно, но субъективно я мог подумать, что разница привела к тому, что один язык, C++, широко осуждается, а другой, Java, широко любят. Возможно, есть место экспертам, которые скажут: «Это идея Плохо».
@Software Monkey: «C++, широко осуждаемый, по сравнению с другим, Java, широко любимым». Это маркетинговая шумиха. Помните, что C++ вырос сам по себе, тогда как Java (и .NET) наживались на маркетинговых бульдозерах. Не кажется ли странным, что для «широко известного языка» Java ограничена серверными приложениями, тогда как «широко осуждаемый» (вероятно, разработчиками и менеджерами Java, желающими снизить стоимость разработки кода) C++ выходит из очень высокого уровня. производительность серверов в высокопроизводительные игры? [...]
@Software Monkey: [...] Мое собственное сравнение между Java / .NET и C++ было не так благоприятно для Java / .NET. Подводя итог: на Java / .NET проще и быстрее создавать код среднего качества, чем на C++, но в тот момент, когда вам нужен высокопроизводительный код от отличного до идеального, Java / .NET будет мешать вам, тогда как C++ сделает это легко. Для получения дополнительной информации об этом см. Мой ответ по адресу: stackoverflow.com/questions/145110/c-performance-vs-java-c/…
«Или не делать вещей ... просто потому, что они лаконичны»? Извините за то, что вкладываю слова в ваш рот, Джеймс Гослинг, но это один из принципов дизайна Java, с которым я не согласен на уровне использования. Конечно, на уровне проектирования и реализации языка, поскольку это значительно упрощает работу специалистов по сопровождению языка.
Мне нравится этот ответ. Конечно, многим здесь нравится думать, что C++ дает вам больше гибкости, но я бы хотел, чтобы они пошли взглянуть на некоторый код C++, который ужасно скомпонован со странными уловками и «исключительными» функциями языка. Если бы эти функции изначально не были доступны, код был бы написан так же легко, но его было бы намного легче читать и поддерживать.
@Hassan: У каждого языка есть свои хаки, и дженерики Java - отличный тому пример. Теперь о 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++.
@paercebal Конечно, я согласен, что на каждом языке есть свои хаки. Я не спорю в пользу того или иного языка, просто у каждого языка есть свои преимущества и недостатки, что делает его более подходящим для определенных задач. Я считаю, что Java - явный победитель с возможностью повторного использования кода, потому что он требует от программиста следовать тем же шаблонам организации, частично путем удаления исключительных функций в самом языке. В своем ответе вы сказали, что нельзя злоупотреблять перегрузкой операторов. К сожалению, всегда найдется кто-то, кто злоупотребляет этой функцией и другими, делая код недоступным для обслуживания.
@Hassan: Странная вещь о «злоупотреблении перегрузкой операторов» заключается в том, что я видел только два случая этого за свою карьеру (12 лет C++), и код не был неприемлемым. . . Теперь, каждый раз, когда кто-то сообщает о подобном злоупотреблении, это кто-то либо принадлежит к группе C или Java и обычно не может подробно описать пример, с которым он / она лично столкнулся. Для меня это пахнет рационализацией. . . Теперь по поводу «простоты» Java мы согласны. Введите в Google термин "Javaschools", чтобы понять, что на самом деле означает эта "простота": encrypted.google.com/search?q=javaschools
@paercebal Но опять же, эта идея, похоже, сосредоточена на очень конкретных недостатках Java. Я прочитал сообщение в блоге, которое было первым попаданием в этот поиск Google, и автор сосредотачивается на слабостях Java, которые делают его непригодным для определенных задач (он привел в качестве примера операционные системы. Он также упомянул MapReduce, хотя существуют Java-реализации этого ). Во всяком случае, я все еще согласен с этим. Java не подходит для любой работы. Но я не знаю, насколько плохо «Javaschools» генерируют программистов, так как в данный момент я изучаю компьютерные науки. Надеюсь, вы ошиблись!
«По сути, перегрузка операторов отлично подходит для класса, который моделирует какую-то точку, валюту или комплексное число. Но после этого у вас быстро заканчиваются примеры». как насчет свиданий? или один из десятков упорядочиваемых типов данных, о которых разработчики Java никогда не думали?
«По сути, перегрузка операторов отлично подходит для класса, который моделирует какую-то точку, валюту или комплексное число. Но после этого у вас быстро заканчиваются примеры». Как насчет любого типа данных, который вы хотите использовать для сортировки ввода-вывода? Подпрограммы ввода-вывода Java довольно уродливы, особенно со слоями абстракции и базами данных. Кроме того, любой контейнерный класс, в котором вы хотите получить доступ к N-му элементу или выполнить какое-то сопоставление «ключ-> значение», отлично использует перегрузку оператора. Я удивлен, что без перегрузки оператора или значений параметров по умолчанию, кто-либо использует этот язык, особенно если он владеет Oracle.
Многие вещи в Java проистекают из личного восприятия Гослинга, которое не всегда может совпадать с реальностью. Он отказался предоставить беззнаковый тип byte, заявив, что беззнаковые типы вызывают проблемы, тогда как на самом деле единственными неподписанными типами, которые вызывают проблемы, являются те, которые являются больше, чем тип продвижения по умолчанию (тип Byte, конечно, не будет; я не думаю, что какой-либо Паскаль у программистов были проблемы с подписанием Byte). Что касается перегрузки операторов, то я думаю, что для языка ООП, основанного на ссылках, такого как Java, важно иметь оператор сравнения ссылок.
Таким образом, если == будет использоваться как для примитивных сравнений, так и для эталонных сравнений, это сделает большинство других видов перегрузки операторов проблематичными. Конечно, позже Java пошла дальше и испортила это различие, разрешив автоматическую распаковку, но я думаю, что была веская философская причина, по которой язык, который использует ==, как Java, не должен поддерживать перегрузку.
@paercebal Одна из возможных причин отказа от добавления перегрузки оператора сейчас - это compatibility with the previous JVMs. Возникает интересный вопрос: возможна ли перегрузка оператора без изменения JVM? Я утверждаю, что это может быть чисто синтаксический сахар (который может нарушить синтаксис Java, но не JVM)
@ geert3: IMHO, модель, в которой мой код Java 8 должен создавать байт-код, совместимый с Java 1.2, тупой и является причиной огромного технического долга (например, смехотворная реализация дженериков Java). Я должен иметь возможность, как и в .NET, настроить минимальную версию JVM и позволить пользователю либо установить эту JVM (или более новую), либо не использовать мой код.
Гослинг также упустил указатели на функции по той же причине, и посмотрите, где мы находимся сегодня ...
+1 за то, что это единственный верный ответ на этот вопрос. Хотя вид Гослинг может быть спорным, это, безусловно, причина бездействия Явы оператора перегрузки. Это определенно должен быть лучший и принятый ответ!
«По сути, перегрузка операторов отлично подходит для класса, который моделирует какую-то точку, валюту или комплексное число. Но после этого у вас быстро заканчиваются примеры». Добавление к @samboosalis и @ jbo5112: Также подумайте, как часто вы хотите поддерживать перегрузку для типов контейнеров; + для последовательностей (которые фактически предоставляет Java string, но никакая другая последовательность не может), различные операции над наборами (обычно выражаются с помощью перегрузок побитовых операторов, например, | для объединения, & для пересечения, < для is-подмножества и т. д.). Эти перегрузки распространены и хорошо понятны на многих других языках.
Такую штуку стоит добавить только ради доступа к символам в строке, по крайней мере, с помощью []. Серьезно, вы можете сделать это почти на всех других языках
Иногда было бы неплохо иметь перегрузку операторов, классы друзей и множественное наследование.
Однако я по-прежнему считаю, что это хорошее решение. Если бы в Java была перегрузка операторов, мы никогда не могли бы быть уверены в значениях операторов, не просматривая исходный код. В настоящее время в этом нет необходимости. И я думаю, что ваш пример использования методов вместо перегрузки оператора также вполне читаем. Если вы хотите прояснить ситуацию, вы всегда можете добавить комментарий над резкими заявлениями.
// a = b + c
Complex a, b, c; a = b.add(c);
Конечно, как уже упоминалось в другом месте, вы также никогда не можете быть уверены в значении функции добавления.
Правда, меня все еще утешает то, что, по крайней мере, мои операторы жестко запрограммированы. Конечно, наличие функций и их разумное использование пойдут нам только на пользу. Проблема в том, что трудно понять, разумно ли ими воспользовался. И вы разумно согласны с определением. :-)
Комментарий, добавленный для пояснения кода, - это то, как код будет выглядеть на языке, поддерживающем перегрузку операторов. Более того, тот факт, что комментарий написан в терминах операторов, противоречит вашему возражению против перегрузки операторов.
Проверьте Boost.Units: текст ссылки
Он обеспечивает без накладных расходов Анализ размеров за счет перегрузки оператора. Насколько это может быть яснее?
quantity<force> F = 2.0*newton;
quantity<length> dx = 2.0*meter;
quantity<energy> E = F * dx;
std::cout << "Energy = " << E << endl;
на самом деле выводит «Энергия = 4 Дж», что правильно.
«Как именно, если усложняет обслуживание и где вообще этот код запутывает?»
Сказать, что перегрузка оператора приводит к логическим ошибкам типа, что оператор не соответствует логике работы, это все равно что ничего не сказать. Тот же тип ошибки произойдет, если имя функции не подходит для логики работы - так каково же решение: отказаться от возможности использования функции !? Это комичный ответ - «Не подходит для логики работы», каждое имя параметра, каждый класс, функция или что-то еще может быть логически несоответствующим. Я думаю, что эта опция должна быть доступна для респектабельного языка программирования, а те, кто думают, что это небезопасно - эй, нет, оба говорят, что вы должны ее использовать. Возьмем C#. Они опустили указатели, но эй - там есть «небезопасный код» - программируйте как хотите на свой страх и риск.
Некоторые говорят, что перегрузка операторов в Java приведет к запутыванию. Эти люди когда-нибудь останавливались, чтобы посмотреть на какой-нибудь Java-код, выполняющий базовые математические операции, такие как увеличение финансовой стоимости на процент с помощью BigDecimal? .... многословие такого упражнения становится демонстрацией запутанности. Как ни странно, добавление перегрузки операторов в Java позволило бы нам создать наш собственный класс Currency, который сделал бы такой математический код элегантным и простым (менее запутанным).
Технически существует перегрузка операторов на каждом языке программирования, который может работать с разными типами чисел, например целые и действительные числа. Объяснение: Термин «перегрузка» означает, что для одной функции просто существует несколько реализаций. В большинстве языков программирования для оператора + предусмотрены разные реализации, одна для целых чисел, одна для вещественных, это называется перегрузкой оператора.
Сейчас многим кажется странным, что в Java есть перегрузка оператора для оператора + для сложения строк, и с математической точки зрения это было бы действительно странно, но с точки зрения разработчика языка программирования нет ничего плохого в добавлении перегрузки встроенного оператора для оператора + для других классов, например Нить. Однако большинство людей согласны с тем, что если вы добавите встроенную перегрузку для + для String, то, как правило, неплохо предоставить эту функциональность и разработчику.
Совершенно не согласен с ошибкой, что перегрузка оператора запутывает код, поскольку это остается на усмотрение разработчика. Это наивно думать, и, честно говоря, уже стареет.
+1 за добавление перегрузки оператора в Java 8.
Использование Java + для объединения чего-либо строкового, IMHO, довольно отвратительное, как и перегрузка / в C и FORTRAN для целого и дробного деления. Во многих версиях Паскаля использование арифметических операторов для любого числового типа даст результаты, численно эквивалентные приведению операндов к Real, хотя результаты, которые могут не быть целыми числами, должны быть переданы через Trunc или Round, прежде чем их можно будет присвоить целым числам.
Это не повод для отказа, но практический:
Люди не всегда используют его ответственно. Взгляните на этот пример из библиотеки Python scapy:
>>> IP()
<IP |>
>>> IP()/TCP()
<IP frag=0 proto=TCP |<TCP |>>
>>> Ether()/IP()/TCP()
<Ether type=0x800 |<IP frag=0 proto=TCP |<TCP |>>>
>>> IP()/TCP()/"GET / HTTP/1.0\r\n\r\n"
<IP frag=0 proto=TCP |<TCP |<Raw load='GET / HTTP/1.0\r\n\r\n' |>>>
>>> Ether()/IP()/IP()/UDP()
<Ether type=0x800 |<IP frag=0 proto=IP |<IP frag=0 proto=UDP |<UDP |>>>>
>>> IP(proto=55)/TCP()
<IP frag=0 proto=55 |<TCP |>>
Вот объяснение:
The / operator has been used as a composition operator between two layers. When doing so, the lower layer can have one or more of its defaults fields overloaded according to the upper layer. (You still can give the value you want). A string can be used as a raw layer.
Альтернативы встроенной поддержке перегрузки оператора Java
Поскольку в Java нет перегрузки операторов, вы можете изучить следующие альтернативы:
Если кому-то известно о других, прокомментируйте, и я добавлю это в этот список.
Хотя язык Java напрямую не поддерживает перегрузку операторов, вы можете использовать Плагин компилятора Manifold в любом проекте Java, чтобы включить его. Он поддерживает Java 8–13 (текущая версия Java) и полностью поддерживается в IntelliJ IDEA.
Я думаю, что люди, принимающие решения, просто забыли о сложных значениях, матричной алгебре, теории множеств и других случаях, когда перегрузка позволила бы использовать стандартные обозначения, не встраивая все в язык. В любом случае, только математически ориентированное программное обеспечение действительно выигрывает от таких возможностей. Обычному клиентскому приложению они почти никогда не нужны.
Эти аргументы о ненужной обфускации, очевидно, допустимы, когда программист определяет некоторый программно-зависимый оператор, где вместо этого он может быть функцией. Имя функции, если оно четко видно, указывает на то, что она выполняет. Оператор - это функция без читаемого имени.
Java, как правило, разрабатывается с учетом философии, согласно которой некоторая дополнительная многословность не является плохой, поскольку делает код более читаемым. Конструкции, которые делают то же самое, просто имеют меньше кода для ввода, в прошлом назывались «синтаксическим сахаром». Это сильно отличается от философии Python, например, где более короткий почти всегда считается лучшим, даже если он предоставляет меньше контекста для второго читателя.
Также можно прочитать Почему Java не поддерживает перегрузку оператора