Почему Java не предлагает перегрузку операторов?

407

Переходя от C ++ к Java, очевидный вопрос без ответа состоит в том, почему Java не включает перегрузку операторов?

Не Complex a, b, c; a = b + c;намного ли проще, чем Complex a, b, c; a = b.add(c);?

Есть известная причина этого веские аргументы для не позволяя перегружать оператор? Причина произвольна или потеряна во времени?

ренголин
источник
1
@zzzz, мне трудно читать эту статью. Был ли это автоматический перевод, или английский является вторым языком писателя? Я считаю, что обсуждение здесь намного чище.
25
Куча людей, закрывающих это как неконструктивное, этот вопрос дал некоторые из наиболее конструктивного диалога, который я видел в SO. Возможно, это лучший кандидат для programmers.stackexchange.com , но бывают моменты, когда я думаю, что SO чрезмерно пренебрегает более широкими темами.
@NoNaMe это легко, просто мысленно вставить и в - недостающие artlcles мертвый поддавки , что человек либо не является носителем английского языка или программист (или как этот парень, как :) Причина программисты могут падение статьи является то , что он может сделать комментарии короче и легче вписаться в предоставленное место .. оттуда, они просто привыкают к этому. Моя проблема с макетом, так или иначе, я всегда попадаю на этот сайт в поиске Google. К счастью, есть отличное расширение для Chrome под названием Clearly, которое прекрасно форматирует трудно читаемые страницы.
Ycomp
1
Я не вижу причин, почему и как ОП приняла первый ответ? Ответ, написанный @ stackoverflow.com/users/14089/paercebal , превосходен. Это должно быть принято.
Деструктор

Ответы:

13

Предполагая, что вы хотите перезаписать предыдущее значение объекта, на который ссылается 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==оба работают над значениями - копирование значения и сравнения значений соответственно.

Аарон
источник
6
Это довольно просто на самом деле ... Просто делайте как Python и не перегружайте задание.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
225
Этот ответ не отвечает на вопрос вообще. Вы просто жаловались на использование Java знака равенства. Если b + C вернул новый Complex, тогда a = b + c будет совершенно верным, и да, намного проще для чтения. Даже если вы хотите изменить a на месте, a.set (b + c) намного проще для чтения - особенно, если арифметика более чем тривиальна: a.set ((a b + b c) / 5) или a = a.multiply (b) .add (b.multiply (c)). делить (5). Ваш выбор ..
BT
24
Или я думаю .. не ваш выбор, в зависимости от обстоятельств
BT
9
В C ++ шаблоны выражений решают проблему дополнительной копии. По этой причине почти все основные арифметические библиотеки используют эту технику. Кроме того, это не решает вопрос, так как a = b + c является просто синтаксическим сахаром для a.foo (b.bar (c)), который действительно является начальным наблюдением в вопросе.
Kaz Dragon
18
Это не ответ на заданный вопрос. Это чьи-то предположения об определенных различиях между Java и C ++.
Щепурин
805

Есть много постов с жалобами на перегрузку операторов.

Я чувствовал, что должен прояснить концепции «перегрузки операторов», предлагая альтернативную точку зрения на эту концепцию.

Код запутывает?

Этот аргумент является ошибкой.

Обфускация возможна на всех языках ...

Код в C или Java также легко запутать с помощью функций / методов, как в C ++ с помощью перегрузок операторов:

// C++
T operator + (const T & a, const T & b) // add ?
{
   T c ;
   c.value = a.value - b.value ; // subtract !!!
   return c ;
}

// Java
static T add (T a, T b) // add ?
{
   T c = new T() ;
   c.value = a.value - b.value ; // subtract !!!
   return c ;
}

/* C */
T add (T a, T b) /* add ? */
{
   T c ;
   c.value = a.value - b.value ; /* subtract !!! */
   return c ;
}

... даже в стандартных интерфейсах Java

Для другого примера, давайте посмотрим Cloneableинтерфейс в Java:

Вы должны клонировать объект, реализующий этот интерфейс. Но ты мог бы лгать. И создать другой объект. На самом деле, этот интерфейс настолько слаб, что вы можете вернуть другой тип объекта, просто для удовольствия:

class MySincereHandShake implements Cloneable
{
    public Object clone()
    {
       return new MyVengefulKickInYourHead() ;
    }
}

Поскольку Cloneableинтерфейс может быть злоупотреблен / запутан, должен ли он быть запрещен по тем же причинам, что и перегрузка оператора C ++?

Мы можем перегрузить toString()метод MyComplexNumberкласса, чтобы он возвращал строковый час дня. Следует toString()ли запретить перегрузку? Мы могли бы саботировать, MyComplexNumber.equalsчтобы он возвращал случайное значение, изменял операнды ... и т. Д. И т. Д.

В Java, как в C ++ или любом другом языке, программист должен соблюдать минимум семантики при написании кода. Это означает реализацию addфункции, которая добавляет, и Cloneableметод реализации, который клонирует, и ++оператор, который увеличивается.

Что в любом случае запутывает?

Теперь, когда мы знаем, что код можно саботировать даже с помощью нетронутых методов Java, мы можем задаться вопросом о реальном использовании перегрузки операторов в C ++?

Понятная и естественная запись: методы против перегрузки операторов?

Ниже мы сравним, для разных случаев, «один и тот же» код в Java и C ++, чтобы понять, какой стиль кодирования более понятен.

Естественные сравнения:

// C++ comparison for built-ins and user-defined types
bool    isEqual          = A == B ;
bool    isNotEqual       = A != B ;
bool    isLesser         = A <  B ;
bool    isLesserOrEqual  = A <= B ;

// Java comparison for user-defined types
boolean isEqual          = A.equals(B) ;
boolean isNotEqual       = ! A.equals(B) ;
boolean isLesser         = A.comparesTo(B) < 0 ;
boolean isLesserOrEqual  = A.comparesTo(B) <= 0 ;

Обратите внимание, что A и B могут быть любого типа в C ++, если предусмотрены перегрузки операторов. В Java, когда A и B не являются примитивами, код может стать очень запутанным, даже для примитивных объектов (BigInteger и т. Д.) ...

Естественные массивы / контейнерные средства доступа и подписка:

// C++ container accessors, more natural
value        = myArray[25] ;         // subscript operator
value        = myVector[25] ;        // subscript operator
value        = myString[25] ;        // subscript operator
value        = myMap["25"] ;         // subscript operator
myArray[25]  = value ;               // subscript operator
myVector[25] = value ;               // subscript operator
myString[25] = value ;               // subscript operator
myMap["25"]  = value ;               // subscript operator

// Java container accessors, each one has its special notation
value        = myArray[25] ;         // subscript operator
value        = myVector.get(25) ;    // method get
value        = myString.charAt(25) ; // method charAt
value        = myMap.get("25") ;     // method get
myArray[25]  = value ;               // subscript operator
myVector.set(25, value) ;            // method set
myMap.put("25", value) ;             // method put

В Java мы видим, что для каждого контейнера, выполняющего одно и то же (доступ к его содержимому через индекс или идентификатор), у нас есть другой способ сделать это, что сбивает с толку.

В C ++ каждый контейнер использует один и тот же способ доступа к своему контенту благодаря перегрузке операторов.

Естественные продвинутые типы манипуляций

В приведенных ниже примерах используется Matrixобъект, найденный с помощью первых ссылок, найденных в Google для « объекта Java Matrix » и « объекта C ++ Matrix »:

// C++ YMatrix matrix implementation on CodeProject
// http://www.codeproject.com/KB/architecture/ymatrix.aspx
// A, B, C, D, E, F are Matrix objects;
E =  A * (B / 2) ;
E += (A - B) * (C + D) ;
F =  E ;                  // deep copy of the matrix

// Java JAMA matrix implementation (seriously...)
// http://math.nist.gov/javanumerics/jama/doc/
// A, B, C, D, E, F are Matrix objects;
E = A.times(B.times(0.5)) ;
E.plusEquals(A.minus(B).times(C.plus(D))) ;
F = E.copy() ;            // deep copy of the matrix

И это не ограничивается матрицами. В BigIntegerи BigDecimalклассах Java страдают от того же запутанным многословия, в то время как их эквиваленты в C ++ являются ясно , как встроенными типами.

Естественные итераторы:

// C++ Random Access iterators
++it ;                  // move to the next item
--it ;                  // move to the previous item
it += 5 ;               // move to the next 5th item (random access)
value = *it ;           // gets the value of the current item
*it = 3.1415 ;          // sets the value 3.1415 to the current item
(*it).foo() ;           // call method foo() of the current item

// Java ListIterator<E> "bi-directional" iterators
value = it.next() ;     // move to the next item & return the value
value = it.previous() ; // move to the previous item & return the value
it.set(3.1415) ;        // sets the value 3.1415 to the current item

Природные функторы:

// C++ Functors
myFunctorObject("Hello World", 42) ;

// Java Functors ???
myFunctorObject.execute("Hello World", 42) ;

Конкатенация текста:

// C++ stream handling (with the << operator)
                    stringStream   << "Hello " << 25 << " World" ;
                    fileStream     << "Hello " << 25 << " World" ;
                    outputStream   << "Hello " << 25 << " World" ;
                    networkStream  << "Hello " << 25 << " World" ;
anythingThatOverloadsShiftOperator << "Hello " << 25 << " World" ;

// Java concatenation
myStringBuffer.append("Hello ").append(25).append(" World") ;

Хорошо, в Java вы MyString = "Hello " + 25 + " World" ;тоже можете использовать ... Но, подождите секунду: это перегрузка операторов, не так ли? Разве это не обман?

:-D

Общий код?

Одни и те же операнды, модифицирующие общий код, должны использоваться как для встроенных модулей / примитивов (которые не имеют интерфейсов в Java), так и для стандартных объектов (которые не могут иметь правильный интерфейс), и пользовательских объектов.

Например, вычисление среднего значения двух значений произвольных типов:

// C++ primitive/advanced types
template<typename T>
T getAverage(const T & p_lhs, const T & p_rhs)
{
   return (p_lhs + p_rhs) / 2 ;
}

int     intValue     = getAverage(25, 42) ;
double  doubleValue  = getAverage(25.25, 42.42) ;
complex complexValue = getAverage(cA, cB) ; // cA, cB are complex
Matrix  matrixValue  = getAverage(mA, mB) ; // mA, mB are Matrix

// Java primitive/advanced types
// It won't really work in Java, even with generics. Sorry.

Обсуждение перегрузки оператора

Теперь, когда мы увидели справедливые сравнения между кодом C ++, использующим перегрузку операторов, и тем же кодом в Java, теперь мы можем обсудить «перегрузку операторов» как концепцию.

Перегрузка оператора существовала еще до появления компьютеров

Даже за пределами компьютерной науки, есть перегрузка операторов: например, в математике, операторы любят +, -, *и т.д. перегружены.

Действительно, значения +, -, *и т.д. изменяется в зависимости от типов операндов (Числовые, векторы, квантовое волновые функции, матрицы и т.д.).

Большинство из нас, как часть наших научных курсов, изучили множество значений для операторов, в зависимости от типов операндов. Мы нашли их смущающими, их?

Перегрузка оператора зависит от его операндов

Это самая важная часть перегрузки операторов: как и в математике или физике, операция зависит от типов ее операндов.

Итак, знайте тип операнда, и вы будете знать эффект операции.

Даже C и Java имеют (жестко запрограммированную) перегрузку операторов

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

В Java нет арифметики указателей, но кто-то все еще нашел бы, что конкатенация строк без +оператора была бы достаточно нелепой, чтобы оправдать исключение в кредо «перегрузка оператора - зло».

Просто вы, как программист C (по историческим причинам) или Java (по личным причинам , см. Ниже), не можете предоставить свой собственный.

В C ++ перегрузка операторов не является обязательной ...

В C ++ перегрузка операторов для встроенных типов невозможна (и это хорошо), но пользовательские типы могут иметь пользовательские перегрузки операторов.

Как уже говорилось ранее, в C ++ и, в отличие от Java, пользовательские типы не считаются гражданами второго сорта по сравнению со встроенными типами. Таким образом, если встроенные типы имеют операторы, пользовательские типы также должны иметь их.

Истина заключается в том, что, как и toString(), clone(), equals()методы для Java ( т.е. квази-стандарт типа ), C ++ перегрузка оператора настолько части C ++ , что он становится столь же естественно , как исходные операторы C, или ранее упомянутыми методов Java.

В сочетании с шаблонным программированием перегрузка операторов становится широко известным шаблоном проектирования. Фактически, вы не сможете зайти слишком далеко в STL, не используя перегруженные операторы и перегружая операторы для своего собственного класса.

... но этим нельзя злоупотреблять

Перегрузка оператора должна стремиться соблюдать семантику оператора. Не вычитать в +операторе (как в «не вычитать в addфункции» или «вернуть дерьмо в cloneметоде»).

Перегрузка броска может быть очень опасной, потому что она может привести к неясностям. Таким образом, они действительно должны быть зарезервированы для четко определенных случаев. Что же касается &&и ||, никогда не перегружать их , если вы действительно не знаете , что вы делаете, как вы будете терять оценку на короткое замыкание , что нативные операторы &&и ||наслаждаться.

Итак ... Хорошо ... Тогда почему это невозможно в Java?

Потому что Джеймс Гослинг сказал так:

Я исключил перегрузку операторов как личный выбор, потому что видел, как слишком много людей злоупотребляют этим в C ++.

Джеймс Гослинг. Источник: http://www.gotw.ca/publications/c_family_interview.htm

Пожалуйста, сравните текст Гослинга выше со Страуструпом ниже:

Многие дизайнерские решения C ++ коренятся в моей неприязни к тому, чтобы заставлять людей делать что-то определенным образом [...] Часто у меня возникало искушение запретить функцию, которая мне лично не нравилась, я воздерживался от этого, потому что не думал, что имею право навязывать свои взгляды другим .

Бьярне Страуструп. Источник: Дизайн и эволюция C ++ (1.3 Общая информация)

Будет ли перегрузка операторов полезной для 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 ...

Так много языков, так много разных (и иногда противоположных) философий, и все же они все согласны с этим.

Пища для размышлений ...

paercebal
источник
50
Это отличный ответ. Я не согласен с этим, но это все еще отличный ответ. Я думаю, что проблемы, которые возможны при плохих перегрузках, превышают значение хороших перегрузок.
Дуглас Лидер
69
@ Дуглас Лидер: Спасибо! Перегрузка оператора похожа на ООП. В первый раз, когда вы научитесь делать это, вы пишете перегрузки везде, как вы бы помещали базовые классы и наследование везде (например, сладкая ирония, Java API). Но это проходит довольно быстро, и тогда вы цените возможность, не злоупотребляя ею. Мой собственный более чем 10-летний опыт работы с C ++ заключается в том, что число плохих перегрузок, которые я видел как в своем коде, так и в коде от других кодеров, настолько мало, что я считаю, что могу сосчитать их с одной стороны. И это намного меньше, чем общее количество ошибок с переполнением sprintf, strcat, memset и buffer.
paercebal
11
@Douglas Leeder: Я полагаю, после обсуждения этого в другом вопросе SO, что разрыв между «любителями» и «ненавистниками» перегрузки операторов, вероятно, вызван различием в их подходе к коду: «ненавистники» - это больше «функции» это то, что имеет значение », что означает, что они ожидают, что функция будет выполнять только одно и только одно. Таким образом, операторы должны работать в соответствии с языком. «Любители» больше относятся к «объектам, которые должны вести себя», что означает, что они с большей готовностью принимают, что функция (и, следовательно, операторы) может изменять свое поведение в соответствии с типом их параметров.
paercebal
103
Эпический ответ. Один из самых квалифицированных разоблачителей, которые я когда-либо читал.
Себастьян Мах
7
@MaartenBodewes: Все примеры, которые я написал выше, и все, что вас беспокоит, это «как разработчик, вы облажались, потому что личный выбор Гослинга» ? Пожалуйста, напишите свой собственный ответ, защищая точку зрения «вы, разработчики, глупы, пусть гениальные люди сами решают, что вам нужно» . Это обсуждение не имеет смысла.
paercebal
44

Джеймс Гослинг сравнил проектирование Java со следующим:

«Существует такой принцип при переезде, когда вы переезжаете из одной квартиры в другую. Интересный эксперимент состоит в том, чтобы упаковать свою квартиру и положить все в коробки, затем переехать в следующую квартиру и ничего не распаковывать, пока она вам не понадобится. Вы готовите свой первый прием пищи, и вы вытаскиваете что-то из коробки. Затем, через месяц или около того, вы использовали это, чтобы в значительной степени выяснить, какие вещи в вашей жизни вам действительно нужны, а затем вы берете остальную часть вещи - забудьте, насколько вам это нравится или насколько это круто - и вы просто выбрасываете это. Удивительно, как это упрощает вашу жизнь, и вы можете использовать этот принцип во всех вопросах дизайна: не делать вещи только потому, что они Вы крутые или просто потому, что они интересные ".

Вы можете прочитать контекст цитаты здесь

В основном перегрузка операторов отлично подходит для класса, который моделирует какую-то точку, валюту или комплексное число. Но после этого у вас быстро заканчиваются примеры.

Другим фактором было злоупотребление функцией в C ++ разработчиками, перегружающими такие операторы, как '&&', '||', операторы приведения и, конечно, 'new'. Сложность, возникающая из-за того, что это сочетается с передачей по значению и исключениями, хорошо освещена в книге об исключительном языке C ++ .

Гарт Гилмор
источник
6
Не могли бы вы привести пример кода «сложность перегрузки операторов в сочетании с передачей по значению и исключениями»? Несмотря на несколько лет игры с языком, владения и чтения всех эффективных / исключительных книг по C ++, я не понимаю, что вы подразумеваете под этим.
paercebal
60
То, что работает для Джеймса Гослинга, не будет работать для всех. Он невероятно близорук, чтобы экстраполировать свой «интересный» упаковочный эксперимент, чтобы он означал: «Выбросьте все в мире, что мне не нужно, поэтому никто не может использовать этот материал». Он явно не знает, что мне нужно или использовать.
BT
49
@BT: Наиболее enlightning является точка зрения Гослинга по сравнению с точки зрения Страуструпа по этому вопросу: 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
29
@Software Monkey: «C ++, широко осуждаемый против других, Java, широко понравившийся» Это маркетинговый обман. Помните, что C ++ вырос в одиночку, тогда как Java (и .NET) извлекли выгоду из маркетинга бульдозеров. Разве не кажется странным, что для «широко любимого языка» Java ограничивается серверными приложениями, тогда как «широко осуждаемый» (вероятно, разработчиками и менеджерами Java, желающими снизить стоимость производства кода) C ++ исходит из очень высокой производительность серверов для высокопроизводительных игр? [...]
paercebal
16
@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
22

Проверьте 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 Дж», что правильно.

user15793
источник
1
«Как именно, если усложняет обслуживание и где на самом деле этот запутанный код?»
Mooing Duck
13

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

В языке, где каждая переменная объекта на самом деле является ссылкой, перегрузка оператора создает дополнительную опасность быть нелогичной - по крайней мере, для программиста C ++. Сравните ситуацию с перегрузкой оператора C # == и Object.Equalsи Object.ReferenceEquals(или как там это называется).

Себастьян Редл
источник
8

Groovy перегружен оператором и работает в JVM. Если вы не возражаете против падения производительности (которое уменьшается с каждым днем). Это автоматически на основе имен методов. например, «+» вызывает метод «плюс (аргумент)».

Ной
источник
4
Я хотел бы, чтобы все синтаксические языки с перегрузкой операторов использовали бы эту технику. Я никогда не понимал, почему они должны изобрести специальную версию именования и поиска методов. Страуструп не упоминает никаких альтернатив в D & EC ++. Команда C # выбрала правильный подход с синтаксисом Linq ( where ...становится .Where(i => ... ). Если бы они сделали то же самое с арифметическими операторами, многие вещи были бы проще и мощнее. У Java есть преимущество с чистого листа, и оно может сделать это правильно (хотя по религиозным причинам, вероятно, никогда не будет).
Даниэль Уорвикер
@DanielEarwicker, я часто отмечал, что, когда возникают сложные разногласия, люди отмечают мотивации для любой из сторон как «религиозные» по своей природе.
@ Нет, я мог бы жить с ограниченным набором перегрузок операторов, таких как эта, при условии, что для имен методов был специальный тег, который оставлял бы их визуально различимыми. Нечто подобное определению метода __plus () для реализации "+" OL и удалению от перегрузки таких вещей, как приведение типов и даже индексы массива. То, с чем я не хочу жить, - это способ, которым C ++ и C # сочли целесообразным его реализовать.
2
Не ответ На ВМ работает много языков. Перегрузка оператора не должна быть хорошей причиной для переключения языков.
Maarten Bodewes
6

Я думаю, что это, возможно, был осознанный выбор дизайна, чтобы заставить разработчиков создавать функции, имена которых четко отражают их намерения. В C ++ разработчики перегружали бы операторы функциональностью, которая часто не имела бы отношения к общепринятой природе данного оператора, делая почти невозможным определение того, что делает часть кода, не глядя на определение оператора.

user14128
источник
14
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 *приведения и т. Д.)
paercebal
6
-1. Каждая переменная, которую вы назначаете, является символом, так же, как символы арифметического оператора. Используете ли вы фразу для названия этой переменной, одно слово или одну букву, это ваше решение (или ваша команда). Кто сказал, что имеет значение, а что нет? Ответ вам, программист. В чистой математике умножение матриц означает нечто иное, чем умножение двух чисел в основной арифметике. Тем не менее мы используем одинаковые символы для обоих типов умножения.
инженер
2
@paercebal: утверждение, к сожалению, верно. Вы должны смотреть не дальше, чем IOstreams, чтобы увидеть это в действии. К счастью, большинство разработчиков более осторожно изобретают новую семантику для существующих операторов.
Бен Фойгт
5
@BenVoigt: [...] И я даже не упоминаю тот факт, что addфункция действительно может быть неправильно использована (например, умножение или получение мьютекса) ... Злоупотребление, упомянутое пользователем14128, не ограничивается операторами, но есть некоторый патологический страх перед перегрузкой операторов, который, как мне кажется, возник из ранних дней C и C ++, страх, который не изменился прямо в Java, но, к счастью, не вошел в C # ... В конце концов, соблюдая семантику и написание четких функций / операторов - работа разработчика. Не язык.
paercebal
3
@ jbo5112: Пример: cout << f() || g(); круглые скобки не проясняют , они исправляют. И если операторы сдвига битов не подвергаются насилию, они не будут необходимы. Почему cout << (5&3) << endl;лучше чем cout.fmt(5&3)(endl);? Использование оператора вызова функции в переменной-члене функтора было бы бесконечно лучшим проектом для потоков, чем перепрофилирование побитовых операторов только потому, что глиф выглядит красиво. Но это далеко не единственное, что не так с потоками.
Бен Фойгт
5

Ну, вы действительно можете выстрелить себе в ногу с перегрузкой оператора. Как с указателями люди делают глупые ошибки с ними, и поэтому было решено убрать ножницы.

По крайней мере, я думаю, что в этом причина. В любом случае, я на вашей стороне. :)

Sarien
источник
Как, например, эта глупая ошибка ...
Амир Кирш
2
Это очень плохой способ мышления. Вы можете выстрелить себе в ногу, мы скорее порежем вам руки, так что вы не сможете. И конечно мы предполагаем, что вы идиот, который застрелится.
ntj
5

Некоторые люди говорят, что перегрузка операторов в Java приведет к запутыванию. Неужели эти люди когда-нибудь останавливались, чтобы посмотреть на какой-нибудь код Java, выполняющий некоторые базовые математические операции, такие как увеличение финансовой стоимости на процент с помощью BigDecimal? .... многословие такого упражнения становится собственной демонстрацией запутывания. По иронии судьбы, добавление перегрузки операторов в Java позволило бы нам создать наш собственный класс Currency, который сделал бы такой математический код элегантным и простым (менее запутанным).

Volksman
источник
4

Сказать, что перегрузка оператора приводит к логическим ошибкам типа, что оператор не соответствует логике операции, это все равно, что ничего не сказать. Тот же тип ошибки произойдет, если имя функции не подходит для логики работы - так в чем же решение: отбросьте возможность использования функции !? Это комичный ответ - «Не подходит для логики работы», каждое имя параметра, каждый класс, функция или что-либо может быть логически неуместным. Я думаю, что эта опция должна быть доступна на респектабельном языке программирования, и те, кто считает его небезопасным - эй, нет, они оба говорят, что вы должны его использовать. Давайте возьмем C #. Они свалили указатели, но эй - есть заявление «небезопасный код» - программа на ваш страх и риск.

Квант
источник
4

Технически, существует перегрузка операторов в каждом языке программирования, который может работать с различными типами чисел, например, целыми и действительными числами. Пояснение: термин перегрузка означает, что для одной функции существует просто несколько реализаций. В большинстве языков программирования предусмотрены различные реализации для оператора +, одна для целых чисел, одна для вещественных чисел, это называется перегрузкой операторов.

Теперь многим кажется странным, что в Java есть перегрузка операторов для оператора + для добавления строк вместе, и с математической точки зрения это было бы действительно странно, но с точки зрения разработчика языка программирования нет ничего плохого в добавлении встроенной перегрузки операторов для оператора + для других классов, например, String. Тем не менее, большинство людей согласны с тем, что, как только вы добавите встроенную перегрузку для + для String, то, как правило, хорошей идеей будет предоставить эту функциональность и разработчику.

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

+1 за добавление перегрузки операторов в Java 8.

Olai
источник
Использование Java +для конкатенации чего-либо string-ish является ИМХО довольно отвратительным, как и перегрузка /в C и FORTRAN для целого и дробного деления. Во многих версиях Паскаля использование арифметических операторов в любом числовом типе даст результаты, численно эквивалентные приведению к операндам Real, хотя результаты, которые могут не быть целыми числами, должны быть переданы Truncили Roundдо того, как они могут быть присвоены целым числам.
суперкат
2

Если предположить, что Java является языком реализации, тогда a, b и c будут ссылками на тип Complex с начальными значениями null. Также предполагая, что Complex является неизменным как упомянутый BigInteger и аналогичный неизменяемый BigDecimal , я думаю, что вы имеете в виду следующее, поскольку вы присваиваете ссылку на Complex, возвращаемую при добавлении b и c, а не сравниваете эту ссылку с a.

Не является:

Complex a, b, c; a = b + c;

намного проще чем:

Complex a, b, c; a = b.add(c);
Дэвид Шлоснагл
источник
2
Я? ;) Равные могут означать как присваивание, так и сравнение, но = всегда присваивание, а == всегда сравнение. Имена сами могут представлять большие источники ошибок.
1

Иногда было бы неплохо иметь перегрузку операторов, классы друзей и множественное наследование.

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

// a = b + c
Complex a, b, c; a = b.add(c);

источник
12
Конечно, как уже упоминалось в другом месте, вы никогда не можете быть уверены в значении функции add.
Затмение
Правда, мне все еще приятно знать, что по крайней мере мои операторы жестко запрограммированы. Конечно, наличие функций и их разумное использование только пойдет нам на пользу. Проблема в том, что трудно понять, использовал ли кто-то их разумно. И что вы согласны с определением разумно. :-)
1
Комментарий, добавленный для пояснения кода, - это то, как код будет выглядеть на языке, который поддерживает перегрузку операторов. Кроме того, тот факт, что комментарий написан с точки зрения операторов, противоречит вашей оппозиции перегрузке операторов.
Алуан Хаддад
0

Это не веская причина для запрета, а практическая причина:

Люди не всегда используют это ответственно. Посмотрите на этот пример из библиотеки Python:

>>> 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 |>>

Вот объяснение:

Оператор / использовался как оператор композиции между двумя слоями. При этом нижний уровень может иметь одно или несколько полей по умолчанию, перегруженных в соответствии с верхним уровнем. (Вы все еще можете дать желаемое значение). Строка может быть использована в качестве необработанного слоя.

Sarien
источник
0

Альтернативы нативной поддержке перегрузки операторов Java

Поскольку в Java нет перегрузки операторов, вот несколько альтернатив, на которые вы можете посмотреть:

  1. Используйте другой язык. Оба Groovy и Scala имеют перегрузку операторов, и основаны на Java.
  2. Используйте плагин java-oo , который позволяет перегрузить операторы в Java. Обратите внимание, что он НЕ зависит от платформы. Кроме того, он имеет много проблем и не совместим с последними выпусками Java (то есть Java 10). ( Оригинальный источник StackOverflow )
  3. Используйте JNI , собственный интерфейс Java или альтернативы. Это позволяет вам писать методы на C или C ++ (может быть, другие?) Для использования в Java. Конечно, это также не зависит от платформы.

Если кто-то знает о других, пожалуйста, прокомментируйте, и я добавлю его в этот список.

gagarwa
источник
0

Хотя язык Java напрямую не поддерживает перегрузку операторов, вы можете использовать плагин компилятора Manifold в любом проекте Java, чтобы включить его. Он поддерживает Java 8 - 13 (текущая версия Java) и полностью поддерживается в IntelliJ IDEA.

Скотт
источник