Чому Java не пропонує оператору перевантажувати?


406

Починаючи з 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. На щастя, є чудове хромоване розширення, яке називається Очевидно, що важко переформатує важко читати сторінки.
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
Ця відповідь зовсім не відповідає на питання. Ви просто втручаєтесь у використанні Явою знака рівності. Якби b + C повернув новий Комплекс, то a = b + c було б цілком справедливим, і так набагато простішим для читання. Навіть якщо ви хотіли змінити місце на місці, 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 ++.
SChepurin

804

Дуже багато повідомлень скаржиться на перевантаження оператора.

Я відчув, що мені доведеться уточнити поняття "перевантаження оператора", пропонуючи альтернативну точку зору на цю концепцію.

Код обманюючий?

Цей аргумент є помилкою.

Обманювання можливе на всіх мовах ...

Заблудити код на 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 " та " Об'єкт матриці C ++ ":

// 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 мають (жорстко кодований) оператор перевантажений

У C реальна поведінка оператора зміниться відповідно до його операндів. Наприклад, додавання двох цілих чисел відрізняється, ніж додавання двох парних чи навіть одного цілого числа та одного подвійного. Існує навіть цілий арифметичний домен вказівника (без кастингу ви можете додати вказівник ціле число, але ви не можете додати два покажчики ...).

У 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

Порівняйте текст Гослінга вище із Stroustrup нижче:

Багато дизайнерських рішень C ++ мають коріння в моїй неприязні до того, щоб змусити людей робити речі якимось особливим чином [...] Часто мене спокушало заборонити функцію, яку мені особисто не подобалося, я утримався від цього, бо не думав, що маю право насилювати мої погляди на інших .

Bjarne Stroustrup. Джерело: Дизайн та еволюція C ++ (1.3 Загальна інформація)

Чи допоможе оператору перевантаження перевага Java?

Деякі об'єкти мають велику користь від перевантаження оператора (конкретні чи числові типи, такі як BigDecimal, складні числа, матриці, контейнери, ітератори, компаратори, парсери тощо).

У C ++ ви можете отримати вигоду з цієї вигоди через смиренність Струструпа. На Java вас просто накрутили через особистий вибір Гослінга .

Чи можна його додати до Java?

Причинами не додавання перевантаженості операторів зараз на Java можуть бути поєднання внутрішньої політики, алергія на функцію, недовіра розробникам (ви знаєте, диверсанти, які, здається, переслідують команди Java ...), сумісність з попередніми JVM, час написати правильну специфікацію тощо.

Тому не затримуйте дихання в очікуванні цієї функції ...

Але вони роблять це в C # !!!

Так ...

Хоча це далеко не єдина відмінність між двома мовами, ця ніколи мене не розважає.

Судячи з усього, C # люди зі своїм "кожним примітивом є struct, і structвипливає з" Об'єкта " , отримали це правильно під час першої спроби.

І вони роблять це іншими мовами !!!

Незважаючи на всі 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 ...

Стільки мов з такою кількістю різних (а іноді і протилежних) філософій, і все ж вони згодні з цим.

Їжа для роздумів...


50
Це відмінна відповідь. Я не згоден з цим, але це все-таки відмінна відповідь. Я думаю, що проблеми, які можливі при поганих перевантаженнях, перевищують значення хороших перевантажень.
Дуглас Лідер

69
@Douglas Leeder: Дякую! Перевантаження оператора, як OOP. Перший раз, коли ви навчитесь це робити, ви записуєте перевантаження скрізь, як ви б розміщували базові класи та успадкування скрізь (наприклад, солодка іронія, API Java). Але це проходить досить швидко, і тоді ви цінуєте можливість, весь час не зловживаючи ним. Мій власний 10-річний досвід роботи щодо C ++ полягає в тому, що кількість поганих перевантажень, які я бачив як у своєму коді, так і в коді від інших кодерів, настільки мала, я вважаю, що я міг би їх порахувати з одного боку. І це набагато менше, ніж кількість загальних помилок із спринцюванням sprintf, strcat, memset та буфера.
paercebal

11
@Douglas Leeder: Я вважаю, після обговорення цього питання в іншому питанні про те, що розрив між "любителями" та "ненависниками" перевантаження оператора, ймовірно, викликаний різницею в їхньому підході до коду: "Ненависників" більше "функцій те, що має значення ", це означає, що вони очікують, що функція може робити одне, і лише одне. Таким чином, оператори повинні працювати так, як це розроблено мовою. "Любителі" більше стосуються "об'єктів, які повинні поводитись", тобто вони з легкістю сприймають, що функція (і, таким чином, оператори) можуть змінювати свою поведінку відповідно до типу своїх параметрів.
paercebal

103
Епічна відповідь. Один з найбільш кваліфікованих дебютів, які я коли-небудь читав.
Себастьян Мах

7
@MaartenBodewes: Усі приклади, про які я писав вище, і все, що вас турбує, - це "як розробник, ти накручений, бо особистий вибір Гослінга" ? Будь ласка, напишіть власну відповідь, захищаючи кут "ти, розробники, дурні, нехай геніальні люди вирішать для тебе те, що тобі потрібно" . Це обговорення не має жодної мети
paercebal

44

Джеймс Гослінг уподібнював дизайн Java наступному:

"Існує цей принцип щодо переїзду, коли ви переїжджаєте з однієї квартири в іншу квартиру. Цікавий експеримент - запакувати свою квартиру і скласти все в ящики, потім переїхати до наступної квартири і нічого не розпаковувати, поки цього не потрібно. Ви робите свою першу страву, і ви щось витягуєте з коробки. Потім через місяць або близько цього ви використали це, щоб майже зрозуміти, які речі у вашому житті вам справді потрібні, а потім ви берете решту речі - забудьте, як вам це подобається чи як це круто - і ви просто викинете його. Дивно, як це спрощує ваше життя, і ви можете використовувати цей принцип у всіх видах дизайнерських питань: не робіть речі тільки тому, що вони круто або просто тому, що вони цікаві. "

Ви можете прочитати контекст цитати тут

В основному перевантаження оператора чудово підходить для класу, який моделює якусь точку, валюту або комплексне число. Але після цього ви швидко починаєте вичерпати приклади.

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


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 Дж", що є правильним.


1
"Як саме, якщо ускладнює технічне обслуговування, і де на землі цей замовчуючий код?"
Mooing Duck

13

Дизайнери Java вирішили, що перевантаження оператора - це більше проблем, ніж варто. Просто як це.

Мовою, де кожна змінна об'єкта насправді є посиланням, перевантаження оператора отримує додатковий ризик бути досить нелогічним - принаймні для програміста C ++. Порівняйте ситуацію з перевантаженням оператора C # 's == та Object.Equalsта Object.ReferenceEquals(або як воно ще називається).


8

Groovy має перевантаження оператора і працює в JVM. Якщо ви не заперечуєте над хітом виконання (щодня стає меншим). Це автоматично на основі назв методів. наприклад, "+" викликає метод "плюс (аргумент)".


4
Я хочу, щоб усі синтаксичні мови з перевантаженням оператора використовували цю техніку. Я ніколи не розумів, чому їм доводиться вигадувати спеціальну версію методу іменування та пошуку. Stroustrup не згадує жодних альтернатив у D & EC ++. Команда C # прийняла правильний підхід із синтаксисом Linq ( where ...стає .Where(i => ... ). Якби вони зробили те саме з арифметичними операторами, так багато речей було б простішими та потужнішими. Java має перевагу в чистому аркуші і може отримати це право (хоча з релігійних причин, мабуть, ніколи не буде).
Даніель Ервікер

@DanielEarwicker, я часто зазначав, що коли виникають складні розбіжності, люди позначають мотивацію обох сторін як "релігійну" природу.

@noah, я міг би жити з обмеженим набором перевантажувальних операторів, таких як цей, за умови наявності спеціального тегу до імен методів, що забезпечує їх візуальне розрізнення. Щось на кшталт визначення методу __plus () для реалізації OL + "+", і залишатися далеко перевантажуючи речі, такі як касти та навіть передплатники масиву. Що я не бажаю жити, - це те, як C ++ і C # бачили, що підходить для його реалізації.

2
Не відповідь. У ВМ працює багато мов. Перевантаження оператора не повинно бути вагомою причиною перемикання мов.
Maarten Bodewes

6

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


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);? Використання оператора виклику функції на змінній члена функтора було б нескінченно кращим дизайном для потоків, ніж перестановка побітових операторів лише тому, що гліф виглядає добре. Але це далеко не єдине, що не так з потоками.
Ben Voigt

5

Добре, ви можете реально стріляти в ногу, перевантажуючи оператора. Це як з покажчиками люди роблять з ними дурні помилки, і тому було вирішено забрати ножиці.

Принаймні, я думаю, що це причина. Я все одно на твоїй стороні. :)



2
Це дуже поганий спосіб мислення. Ви можете стріляти собі в ногу, ми скоріше рубаємо руки вниз, тому вам цього не вдасться. І ми, звичайно, припускаємо, що ви ідіот, який буде стріляти сам.
ntj

5

Деякі люди кажуть, що перевантаження операторами на Java може призвести до затуманення. Чи зупинялися ті люди, щоб переглядати якийсь код Java, виконуючи деякі основні математики, такі як збільшення фінансової вартості на відсоток за допомогою BigDecimal? .... багатослівність такої вправи стає власною демонстрацією затуманення. За іронією долі, додавання перевантаженості оператора на Java дозволить нам створити власний клас валюти, який би зробив такий математичний код елегантним та простим (менш затуманеним).


4

Скажіть, що перевантаження оператора призводить до логічних помилок типу, що оператор не відповідає логіці операції, це як би нічого не говорити. Один і той же тип помилок буде виникати, якщо ім'я функції невідповідне логіці роботи - тож яке рішення: відкиньте можливість використання функції !? Це комічна відповідь - "Невідповідна логіці операції", кожне ім'я параметра, кожен клас, функція або все, що може бути логічно невідповідним. Я думаю, що цей варіант повинен бути доступним на поважній мові програмування, а ті, хто вважає, що це небезпечно, - ні один із обох не каже, що ви повинні ним користуватися. Давайте візьмемо C #. Вони зависли покажчики, але ей - є заява "небезпечний код" - програмуйте як завгодно на свій страх і ризик.


4

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

Зараз багатьом людям здається дивним, що у Java є перевантаження оператора для оператора + для додавання рядків разом, і з математичної точки зору це справді було б дивним, але, як видно з точки зору розробника мови програмування, немає нічого поганого в додаванні вбудованого оператора перевантаження для оператора + для інших класів, наприклад, String. Однак більшість людей сходяться на думці, що як тільки ви додаєте вбудовану перевантаження для + для String, тоді, як правило, хороша ідея забезпечити цю функціональність і для розробника.

Повністю не згоден з помилкою, що оператор перевантажує код обфускує, оскільки це залишається розробнику вирішити. Це наївно думати, і якщо бути чесним, воно старіє.

+1 для додавання оператора перевантаження в Java 8.


Використання Java +для об'єднання будь-якого рядкового результату є IMHO досить огидним, як і перевантаження /C і FORTRAN для цілого та дробового поділу. У багатьох версіях Pascal використання арифметичних операторів на будь-якому числовому типі дасть результати, що чисельно еквівалентні опроданню операндів Real, хоча результати, які можуть бути не цілими числами, повинні бути передані через Truncабо Roundдо того, як вони можуть бути призначені цілим числам.
supercat

2

Якщо припустити, що Java є мовою реалізації, тоді a, b і c будуть посиланнями на тип Complex з початковими значеннями null. Також припускаючи, що Комплекс незмінний як згаданий BigInteger і подібний незмінний BigDecimal , я думаю, ви маєте на увазі наступне, оскільки ви присвоюєте посилання на Комплекс, повернутий із додавання 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
Звичайно, як було зазначено в іншому місці, ви ніколи не можете бути впевнені в значенні функції додавання.
Затемнення

Щоправда, мені все одно втішно знати, що принаймні мої оператори жорстко закодовані. Звичайно, мати функції та розумно використовувати їх тільки нам добре. Проблема в тому, що важко дізнатися, чи хтось їх розумно використав. І що ви поважно погоджуєтесь з визначенням. :-)

1
У коментарі, доданому для уточнення коду, те, як виглядатиме код на мові, яка підтримує перевантаження оператора. Крім того, той факт, що коментар написаний з точки зору операторів, викликає вашу опозицію до перевантаження оператора.
Алуан Хаддад

0

Це не вагомий привід заборонити це, але практичний:

Люди не завжди використовують це відповідально. Подивіться на цей приклад з бібліотеки 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 |>>

Ось пояснення:

Оператор / використовувався як оператор композиції між двома шарами. При цьому нижній шар може мати одне або більше своїх за замовчуванням полів, перевантажених відповідно до верхнього шару. (Ви все одно можете дати бажане значення). Рядок можна використовувати як необроблений шар.


0

Альтернативи нативній підтримці перевантаження оператора Java

Оскільки у Java немає перевантаження оператора, ось кілька альтернатив, які ви можете переглянути:

  1. Використовуйте іншу мову. І Groovy, і Scala мають перевантаження оператора, і базуються на Java.
  2. Використовуйте java-oo , плагін, який дозволяє оператору перевантажувати Java. Зауважте, що це НЕ платформа незалежна. Крім того, у нього багато проблем, і він не сумісний з останніми випусками Java (тобто Java 10). ( Оригінальне джерело StackOverflow )
  3. Використовуйте JNI , Java Native Interface або альтернативи. Це дозволяє писати методи C або C ++ (можливо, інші?) Для використання на Java. Звичайно, це також НЕ залежить від платформи.

Якщо хтось знає про інших, будь ласка, прокоментуйте, і я додам його до цього списку.


0

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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.