Java neden operatör aşırı yüklemesi sunmuyor?


406

C ++ 'dan Java'ya gelince, cevaplanmamış soru şu: Java neden operatör aşırı yüklemesini içermiyordu?

Bundan Complex a, b, c; a = b + c;daha basit değil Complex a, b, c; a = b.add(c);mi?

Bunun, operatörün aşırı yüklenmesine izin vermemesinin geçerli bir argümanı olduğu bilinen bir nedeni var mı ? Nedeni keyfi mi yoksa zamanla mı kayboluyor?



1
@zzzz, bu makaleyi okumakta zorlandım. Bu otomatik olarak mı çevrildi, yoksa İngilizce yazarın 2. dili mi? Burada tartışmayı daha temiz buluyorum.

25
Bunu yapıcı olmayan olarak kapatan millet yığını için, bu soru SO'da gördüğüm en yapıcı diyalogdan bazılarını getirdi. Belki de programcılar için daha iyi bir adaydır .

Sadece zihinsel eklemek, kolay @NoNaMe bir ve - Eksik artlcles kişi bir yerli ingilizce konuşabilirsiniz veya programcı (ya olmadığını ölü bir hediye olduğunu ya bu adam gibi, hem :) makaleler bırakabilirsiniz nedeni programcılar olabildiğince olmasıdır sağlanan alanda yorumları daha kısa ve daha kolay hale getirin .. oradan, sadece alışırlar. Benim sorunum düzeni ile, bir şekilde ben her zaman google aramalarında bu siteye isabet. Neyse ki Clearly adlı harika bir krom uzantısı var .
ycomp

1
OP'nin ilk cevabı neden & nasıl kabul ettiğine dair bir neden göremiyorum? Tarafından cevap @ stackoverflow.com/users/14089/paercebal mükemmel. Kabul edilmelidir.
Yıkıcı

Yanıtlar:


13

Tarafından belirtilen nesnenin önceki değerinin üzerine yazmak istediğinizi varsayarsak a, bir üye işlevinin çağrılması gerekir.

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

C ++ 'da, bu ifade derleyiciye yığın üzerinde üç (3) nesne oluşturmasını, ekleme yapmasını ve sonuç değerini geçici nesneden varolan nesneye kopyalamasını söyler a.

Ancak, Java'da operator=referans türleri için değer kopyalaması gerçekleştirmez ve kullanıcılar değer türleri için değil, yalnızca yeni başvuru türleri oluşturabilir. Dolayısıyla, kullanıcı tanımlı bir tür için Complexatama, varolan bir değere başvuru kopyalamak anlamına gelir.

Bunun yerine şunu düşünün:

b.set(1, 0); // initialize to real number '1'
a = b; 
b.set(2, 0);
assert( !a.equals(b) ); // this assertion will fail

C ++ 'da bu değeri kopyalar, böylece karşılaştırma eşit olmaz. Java'da, operator=referans kopyasını gerçekleştirir ave bşimdi aynı değere başvuruyor . Sonuç olarak, karşılaştırma 'eşit' üretecektir, çünkü nesne kendisiyle eşit karşılaştırır.

Kopyalar ve referanslar arasındaki fark, yalnızca operatörün aşırı yüklenmesi ile karışır. @Sebastian'ın belirttiği gibi, Java ve C # değer ve referans eşitliğini ayrı ayrı ele almak zorundadır - operator+büyük olasılıkla değerler ve nesnelerle ilgilenir, ancak operator=referanslarla ilgilenmek için zaten uygulanır.

C ++ 'da, aynı anda yalnızca bir tür karşılaştırma ile uğraşmalısınız, böylece daha az kafa karıştırıcı olabilir. Örneğin Complex, operator=ve operator==değerleri üzerinde çalışıyor - sırasıyla değerleri kopyalama ve değerleri karşılaştırma.


6
Gerçekten çok basit ... Sadece Python gibi ve aşırı bir görev yok.
L̲̳̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

225
Bu cevap soruya hiç cevap vermiyor. Sadece Java'nın eşittir işaretini kullanması üzerinde duruyorsunuz. B + C yeni bir Kompleks döndürdüyse, a = b + c mükemmel bir şekilde geçerli olur ve okunması çok daha kolaydır. Bir yerinde değiştirmek isteseniz bile, a.set (b + c) okunması daha basittir - özellikle aritmetik önemsiz olduğunda: a.set ((a b + b c) / 5) veya a = a. çoklu (b). ekle (b. çoklu (c)). bölme (5). Seçiminiz ..
BT

24
Ya da sanırım .. senin seçimin değil, duruma göre
BT

9
C ++ 'da İfade Şablonları ekstra kopya sorununu çözer. Hemen hemen tüm büyük aritmetik kütüphaneleri bu tekniği bu nedenle kullanırlar. Ayrıca, bu soruya değinmez, çünkü a = b + c sadece sorudaki ilk gözlem olan a.foo (b.bar (c)) için sözdizimsel şekerdir.
Kaz Dragon

18
Sorulan sorunun cevabı bu değil. Bu birinin Java ve C ++ arasındaki bazı farklar hakkındaki spekülasyonlarıdır.
SChepurin

804

Operatörün aşırı yüklenmesi hakkında şikayet eden birçok yayın var.

Bu operatöre alternatif bir bakış açısı sunarak "operatör aşırı yüklenmesi" kavramlarını açıklığa kavuşturmak zorunda olduğumu hissettim.

Kod gizliyor mu?

Bu argüman yanlıştır.

Tüm dillerde gizlemek mümkündür ...

C ++ veya Java'daki kodları işleçler / yöntemlerle gizlemek, C ++ 'da olduğu gibi operatör aşırı yüklemeleriyle gizlemek kolaydır:

// 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'nın standart arayüzlerinde bile

Başka bir örnek olarak, Java'daki Cloneablearayüzü görelim :

Bu arabirimi uygulayan nesneyi klonlamanız gerekiyor. Ama yalan söyleyebilirsin. Ve farklı bir nesne oluşturun. Aslında, bu arayüz o kadar zayıf ki, sadece eğlenmek için başka bir nesne türünü tamamen geri döndürebilirsiniz:

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

Gibi Cloneablearayüz kötüye kullanılabilir / şaşırtma, bu C ++ operatör yükleme olması gerekiyordu aynı gerekçelerle yasaklı olmalıdır?

Günün dizeli saatini döndürmesi için sınıfın toString()yöntemini aşırı MyComplexNumberyükleyebiliriz. Meli toString()aşırı yükleme de yasaklanmalı? MyComplexNumber.equalsRastgele bir değer döndürmesi, işlenenleri değiştirmesi ... vb. Vs .. için sabotaj yapabiliriz .

Java'da, C ++ veya herhangi bir dilde olduğu gibi, programcı kod yazarken minimum semantiğe saygı göstermelidir. Bu add, ekleyen bir işlev Cloneableuygulamak ve klonlayan uygulama yöntemi ve ++artışlardan daha fazla bir işleç anlamına gelir .

Zaten ne şaşırttı?

Artık kodun bozulmamış Java yöntemleriyle bile sabote edilebildiğini bildiğimize göre, kendimize C ++ 'da aşırı operatörün gerçek kullanımı hakkında soru sorabilir miyiz?

Açık ve doğal gösterim: yöntemler ve operatör aşırı yüklemesi?

Farklı durumlarda, hangi kodlama stilinin daha net olduğu hakkında bir fikre sahip olmak için Java ve C ++ 'daki "aynı" kodu karşılaştıracağız.

Doğal karşılaştırmalar:

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

Operatör aşırı yükleri sağlandığı sürece A ve B'nin C ++ 'da herhangi bir türde olabileceğini lütfen unutmayın. Java'da, A ve B ilkel olmadığında, kod ilkel benzeri nesneler için bile (BigInteger, vb.) Çok kafa karıştırıcı olabilir.

Doğal dizi / konteyner erişimcileri ve abonelik:

// 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'da, her bir kapsayıcı için aynı şeyi yapma (içeriğine bir dizin veya tanımlayıcı aracılığıyla erişme) için bunu yapmak için farklı bir yolumuz olduğunu görüyoruz, bu da kafa karıştırıcı.

C ++ 'da, her kapsayıcı, operatörün aşırı yüklenmesi sayesinde içeriğine erişmek için aynı yolu kullanır.

Doğal ileri tip manipülasyon

Aşağıdaki örneklerde Matrix, " Java Matrix nesnesi " ve " C ++ Matrix nesnesi " için Google'da bulunan ilk bağlantılar kullanılarak bulunan bir nesne kullanılır :

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

Ve bu sadece matrislerle sınırlı değil. BigIntegerVe BigDecimalJava sınıfları C onların benzerleri ++ gibidir oysa aynı kafa karıştırıcı ayrıntı muzdarip dahili içinde türleri gibi berrak.

Doğal yineleyiciler:

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

Doğal fonksiyonlar:

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

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

Metin birleştirme:

// 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") ;

Tamam, Java'da da kullanabilirsiniz MyString = "Hello " + 25 + " World" ;... Ama bir saniye: Bu operatörün aşırı yüklenmesi, değil mi? Hile değil mi ???

:-D

Genel kod mu?

Aynı genel kod değiştiren işlenenler hem yerleşik / temel öğeler (Java'da arabirimi olmayan), standart nesneler (doğru arabirime sahip olamayan) hem de kullanıcı tanımlı nesneler için kullanılabilir olmalıdır.

Örneğin, rastgele türlerin iki değerinin ortalama değerinin hesaplanması:

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

Operatör aşırı yüklemesi tartışılıyor

Artık, operatör aşırı yüklemesi kullanan C ++ kodu ile Java'daki aynı kod arasında adil karşılaştırmalar gördüğümüze göre, artık "operatör aşırı yüklemesi" ni bir kavram olarak tartışabiliriz.

Operatör aşırı yüklemesi, bilgisayarlardan önce var oldu

Hatta dış bilgisayar biliminin, operatör yükleme vardır: Örneğin, matematik, operatörler gibi +, -, *aşırı yüklü vb.

Gerçekten de, anlam +, -, *vb değişiklikler işlenen türleri (vs rakamsal, vektörler, kuantum dalga fonksiyonları, matrisler,) bağlı olarak değişebilir.

Bilim kurslarımızın bir parçası olarak çoğumuz, işlenen türlerine bağlı olarak operatörler için birçok anlam öğrendik. Onları kafa karıştırıcı bulduk mu?

Operatör aşırı yüklenmesi, operandlarına bağlıdır

Bu, operatörün aşırı yüklenmesinin en önemli parçasıdır: Matematikte veya fizikte olduğu gibi, işlem, işlenen tiplerine bağlıdır.

Yani, işlenenin türünü bilin ve işlemin etkisini bileceksiniz.

C ve Java bile (sabit kodlanmış) operatör aşırı yüklemesine sahiptir

C'de, bir operatörün gerçek davranışı, işlenenlerine göre değişecektir. Örneğin, iki tamsayı eklemek iki kat, hatta bir tamsayı ve bir çift eklemekten farklıdır. İşaretçi aritmetik etki alanının tamamı bile vardır (döküm olmadan, bir işaretçiye bir tamsayı ekleyebilirsiniz, ancak iki işaretçi ekleyemezsiniz).

Java'da, işaretçi aritmetiği yoktur, ancak birileri hala +operatör olmadan dize birleştirme bulmuş , "operatör aşırı yüklenmesi kötü" inancında bir istisnayı haklı çıkaracak kadar saçma olacaktır.

Sadece bir C (tarihsel nedenlerden dolayı) veya Java ( kişisel nedenlerden dolayı aşağıya bakın) kodlayıcısı olarak kendinizinkini sağlayamazsınız.

C ++ 'da, operatör aşırı yüklenmesi isteğe bağlı değildir ...

C ++ 'da, yerleşik türler için operatör aşırı yüklemesi mümkün değildir (ve bu iyi bir şeydir), ancak kullanıcı tanımlı türlerde kullanıcı tanımlı operatör aşırı yüklemeleri olabilir.

Daha önce de belirtildiği gibi, C ++ 'da ve Java'nın aksine, kullanıcı türleri yerleşik türlerle karşılaştırıldığında dilin ikinci sınıf vatandaşları olarak kabul edilmez. Bu nedenle, yerleşik türlerin operatörleri varsa, kullanıcı türlerinin de sahip olması gerekir.

Gerçek şu ki gibi toString(), clone(), equals()yöntemler Java içindir ( yani yarı-standart-benzeri ), C ++ operatör aşırı yükleme o sözü Java yöntemlerine önce orijinal C operatörleri gibi doğal olarak ya da olur C ++ kadar çok parçasıdır.

Şablon programlama ile birleştirildiğinde, operatör aşırı yüklenmesi bilinen bir tasarım deseni haline gelir. Aslında, aşırı yük operatörleri ve kendi sınıfınız için aşırı yük operatörleri kullanmadan STL'de çok ileri gidemezsiniz.

... ama istismar edilmemeli

Operatör aşırı yüklemesi, operatörün anlambilimine saygı göstermeye çalışmalıdır. Bir +işleci çıkarmayın ("bir addişlevde çıkartmayın " veya "bir cloneyöntemde bok dönüşü " de olduğu gibi).

Döküm aşırı yüklenmesi, belirsizliğe yol açabileceğinden çok tehlikeli olabilir. Bu yüzden gerçekten iyi tanımlanmış vakalar için ayrılmalıdır. Gelince &&ve ||gerçekten ne yaptığınızı bilmiyorsanız yerli operatörler bu kısa devre değerlendirmesini kaybedersiniz olarak, şimdiye kadar, onları aşırı değil &&ve ||tadını çıkarın.

Öyleyse ... Tamam ... Öyleyse neden Java'da mümkün değil?

Çünkü James Gosling şöyle dedi:

Operatör aşırı yüklemeyi oldukça kişisel bir seçenek olarak bıraktım çünkü C ++ 'da çok fazla insanı kötüye kullandım.

James Gosling. Kaynak: http://www.gotw.ca/publications/c_family_interview.htm

Lütfen Gosling'in yukarıdaki metnini Stroustrup'un metinleriyle karşılaştırın:

Birçok C ++ tasarım kararının kökenleri, insanları belirli bir şekilde bir şeyler yapmaya zorlamaktan hoşlanmıyor [...] Genellikle, kişisel olarak sevmediğim bir özelliği yasadışı bırakmak istedim, çünkü sahip olduğumu düşünmedim başkaları hakkındaki görüşlerimi zorlama hakkı .

Bjarne Stroustrup. Kaynak: C ++ Tasarımı ve Evrimi (1.3 Genel Altyapı)

Operatörün aşırı yüklenmesi Java'ya fayda sağlar mı?

Bazı nesneler operatör aşırı yüklenmesinden büyük ölçüde yararlanır (BigDecimal, karmaşık sayılar, matrisler, kaplar, yineleyiciler, karşılaştırıcılar, ayrıştırıcılar vb. Gibi somut veya sayısal türler).

C ++ 'da Stroustrup'un alçakgönüllülüğü nedeniyle bu avantajdan yararlanabilirsiniz. Java'da Gosling'in kişisel tercihi yüzünden mahvoldunuz .

Java'ya eklenebilir mi?

Şu anda Java'da aşırı yükleme yapmamanın nedenleri, iç politika, özelliğe alerji, geliştiricilere güvensizlik (biliyorsunuz, Java ekiplerine musallat görünen saboteurlar ...), önceki JVM'lerle uyumluluk, Doğru bir şartname yazma zamanı vs.

Bu yüzden bu özelliği beklerken nefesinizi tutmayın ...

Ama bunu C # !!!

Evet...

Bu iki dil arasındaki tek fark olmaktan uzak olsa da, bu beni asla eğlendiremiyor.

Görünüşe göre, C # millet, onların "her ilkel bir structve structNesneden türetir" ile ilk denemede doğru anladım.

Ve bunu diğer dillerde yapıyorlar !!!

Kullanılan tanımlanmış operatör aşırı yüklemesine karşı tüm FUD'lara rağmen, aşağıdaki diller bunu destekler: 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 ...

Pek çok dil, pek çok farklı (ve bazen karşıt) felsefeye sahip ve yine de hepsi bu noktada hemfikir.

Düşünce için yiyecek ...


50
Bu mükemmel bir cevap. Buna katılmıyorum, ama yine de mükemmel bir cevap. Kötü aşırı yüklenmelerde mümkün olan sorunların iyi aşırı yüklenmelerin değerini aştığını düşünüyorum.
Douglas Leeder

69
@ Leuglas Leeder: Teşekkürler! Operatör aşırı yüklenmesi OOP gibidir. İlk yapmayı öğrendiğinizde, temel sınıfları ve kalıtımı her yere (tatlı ironi, Java API gibi) koyduğunuz gibi her yere aşırı yükler yazıyorsunuz. Ancak bu oldukça hızlı bir şekilde geçer ve daha sonra kötüye kullanmama ihtimalini takdir edersiniz. C ++ ile ilgili kendi 10 yıllık deneyimim, hem kodumda hem de diğer kodlayıcılardan kodda gördüğüm kötü aşırı yüklerin sayısının çok düşük olması, onları bir yandan sayabileceğime inanıyorum. Ve bu, sprintf, strcat, memset ve tampon taşması olan genel hata sayısından çok daha azdır.
paercebal

11
@ Douglas Leeder: Başka bir SO sorusunda bu konuyu tartıştıktan sonra, operatör aşırısının "sevenler" ve "nefret edenler" arasındaki boşluğun muhtemelen kod yaklaşımındaki bir farktan kaynaklandığına inanıyorum: "Haters" daha fazla "fonksiyonlar önemli olan şeydir ", yani bir işlevin tek bir şey yapmasını bekledikleri anlamına gelir. Bu nedenle operatörler dil tarafından tasarlandığı şekilde çalışmalıdır. "Aşıklar" daha çok "nesneler davranmalı" dır, yani işlevin (ve böylece işleçlerin) davranışlarını parametrelerinin türüne göre değiştirebileceğini daha kolay kabul ederler.
paercebal

103
Destansı bir cevap. Şimdiye kadar okuduğum en nitelikli anlatımlardan biri.
Sebastian Mach

7
@MaartenBodewes: Yukarıda yazdığım tüm örnekler ve sizi rahatsız eden tek şey "bir geliştirici olarak, Gosling'in kişisel tercihi yüzünden mahvoldun" mu? Lütfen, "siz geliştiriciler aptalsınız, dahi insanlar sizin için neye ihtiyacınız olduğuna karar verin" açısını savunarak kendi cevabınızı yazın . Bu tartışma hiçbir amaca hizmet etmiyor.
paercebal

44

James Gosling, Java tasarımını aşağıdakilere benzetmiştir:

"Bir daireden başka bir daireye taşındığınızda, taşınmayla ilgili bu prensip var. İlginç bir deney, dairenizi toplayıp her şeyi kutulara koymak, sonra bir sonraki daireye taşınmak ve ihtiyacınız olana kadar hiçbir şeyi açmamaktır. İlk yemeğinizi hazırlıyorsunuz ve bir kutudan bir şeyler çıkarıyorsunuz.Ardından bir ay sonra, hayatınızda gerçekten hangi şeylere gerçekten ihtiyacınız olduğunu anlamak için kullandınız ve geri kalanını şeyler - ne kadar sevdiğini ya da ne kadar havalı olduğunu unutma - ve sadece atarsın.Bu hayatınızı nasıl basitleştiriyor ve bu prensibi her türlü tasarım sorununda kullanabilirsiniz: havalı ya da ilginç oldukları için. "

Teklifin içeriğini buradan okuyabilirsiniz

Temel olarak operatör aşırı yüklenmesi, bir tür nokta, para birimi veya karmaşık sayı modelleyen bir sınıf için mükemmeldir. Ancak bundan sonra örneklerin hızlıca tükenmesine başlarsınız.

Diğer bir faktör ise '++', '||', oyuncular ve elbette 'yeni' gibi operatörleri aşırı yükleyen geliştiriciler tarafından C ++ 'daki özelliğin kötüye kullanılmasıydı. Bunu değer ve istisnalar ile birleştirmekten kaynaklanan karmaşıklık, Olağanüstü C ++ kitabında iyi bir şekilde ele alınmıştır .


6
"Operatör aşırı yüklemesinin karmaşıklığı, by-pass değeri ve istisnalarla birlikte" kod örneğini verebilir misiniz? Birkaç yıl dil ile oynamaya ve C ++ ile ilgili tüm etkili / istisnai kitapları okuyup okumasına rağmen, bununla ne demek istediğini anlayamıyorum.
paercebal

60
James Gosling için işe yarayan herkes için işe yaramaz. "İlginç" paketleme denemesini "Dünyada ihtiyacım olmayan her şeyi atın, böylece hiç kimse bu şeyleri kullanamaz" demek için inanılmaz derecede uzadı. Neye ihtiyacım olduğunu veya kullandığımı açıkça bilmiyor.
BT

49
@BT: En aydınlatıcı, Stroustrup'un bu konudaki bakış açısıyla karşılaştırıldığında Gosling'in bakış açısı 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
@Yazılım Maymun: "C ++, yaygın reviled vs Java, yaygın sevdim" Bu pazarlama hype olduğunu. C ++ 'ın tek başına büyüdüğünü, Java (ve .NET) pazarlama buldozerlerinden yararlandığını unutmayın. "Yaygın olarak sevilen bir dil" için Java'nın sunucu uygulamalarıyla sınırlı olması garip değil mi, oysa "yaygın olarak revize edilir" (muhtemelen Java geliştiricileri ve kod üretim maliyetini düşürmek isteyen yöneticiler tarafından) C ++ çok yüksek yüksek performanslı oyunlara performans sunucuları? [...]
paercebal

16
@Hassan: Her dilin hackleri var, Java'nın jenerikleri bunun harika bir örneği. Şimdi, hakkında 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: Kötü programcılar dil ne olursa olsun kötü kod yazacak. Java'daki işlev parametrelerinin fikir sahibi olması için bir "doğrudan referans" ifadesini taklit etmeye çalışın. Kodu gördüm ve çok zor güldü, incindi. Bu Gosling'in kullanmadığı şeylerdir, bu nedenle Java'da olması için korkunç saldırılara ihtiyaç duyulur, ancak hem C # hem de C ++ 'da doğal olarak sıfır maliyetle bulunur.
paercebal

22

Boost.Nits'e göz atın: bağlantı metni

Operatör aşırı yüklemesi ile sıfır tepegöz Boyut analizi sağlar. Bu ne kadar netleşebilir?

quantity<force>     F = 2.0*newton;
quantity<length>    dx = 2.0*meter;
quantity<energy>    E = F * dx;
std::cout << "Energy = " << E << endl;

aslında doğru olan "Enerji = 4 J" çıktı.


1
"Bakımı tam olarak nasıl karmaşıklaştırıyorsa ve bu nerede kodlamayı gizliyor?"
Mooing Ördek

13

Java tasarımcıları, operatör aşırı yüklemesinin değerinden daha fazla sorun olduğuna karar verdi. Bu kadar basit.

Her nesne değişkeninin aslında bir referans olduğu bir dilde, aşırı yük operatörü, en azından bir C ++ programcısı için oldukça mantıksız olma tehlikesini alır. Durumu C # 's == operatör aşırı yüklenmesi ve Object.Equalsve Object.ReferenceEquals(veya ne denirse) ile karşılaştırın.


8

Groovy , operatörün aşırı yüklenmesine sahiptir ve JVM'de çalışır. Performans hit sakıncası yoksa (ki bu her gün küçülüyor). Yöntem adlarına göre otomatiktir. örneğin, '+' 'artı (bağımsız değişken)' yöntemini çağırır.


4
Keşke operatör aşırı yüklemeli tüm sözdizimi ağır diller bu tekniği kullansaydı. Neden yöntem adlandırma ve aramanın özel bir sürümünü icat etmek zorunda olduklarını hiç anlamadım. Stroustrup, D & EC ++ 'da herhangi bir alternatiften bahsetmez. C # takımı Linq sözdizimi (sağ yaklaşım aldı where ...hale .Where(i => ... ). Sadece aritmetik operatörlerle aynı şeyi yapsaydı, birçok şey daha basit ve daha güçlü olurdu. Java, temiz bir sayfa avantajına sahiptir ve bunu doğru bir şekilde alabilir (dini nedenlerden dolayı, muhtemelen asla olmayacaktır).
Daniel Earwicker

@DanielEarwicker, çoğu zaman insanların her iki tarafın motivasyonlarını doğada "dini" olarak etiketleyecekleri karmaşık anlaşmazlıklar olduğunda da belirtmiştim.

@noah, yöntem adlarının görsel olarak farklı olmasını sağlayan özel bir etiket olması şartıyla, bunun gibi sınırlı bir operatör aşırı yük alt kümesiyle yaşayabilirdim. Bir "+" OL uygulaması için __plus () yöntemini tanımlamak ve dökümler ve hatta dizi abonelikleri gibi aşırı yükleri uzak tutmak gibi bir şey. Yaşamak istemediğim şey C ++ ve C # 'ın onu uygulamak için uygun gördüğü yoldur.

2
Cevap değil. VM'de çalışan birçok dil var. Operatör aşırı yüklenmesi, dil değiştirmek için kendi başına iyi bir neden olmamalıdır.
Maarten Bodewes

6

Sanırım bu, geliştiricileri isimlerini niyetlerini açıkça ileten işlevler yaratmaya zorlamak için bilinçli bir tasarım seçimi olabilir. C ++ geliştiricileri, genellikle belirli bir kod parçasının operatörün tanımına bakmadan ne yaptığını belirlemeyi neredeyse imkansız hale getirerek, işleçleri genellikle verilen operatörün yaygın olarak kabul edilen doğasıyla hiçbir ilişkisi olmayacak işlevsellik ile aşırı yükler.


14
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator: Bu nedensiz bir iddia. 12 yıldır profesyonel bir C ++ geliştiricisiyim ve nadiren bu sorunla karşılaştım. Aslında, C ++ 'da gördüğüm en çok hata ve tasarım hataları C tarzı kodda ( void *, dökümler, vb.)
İdi

6
-1. Atadığınız her değişken, tıpkı aritmetik işleç sembolleri gibi bir semboldür. Bu değişkeni, tek bir kelimeyi veya tek bir harfi adlandırmak için bir cümle kullanmak, sizin (veya ekibinizin) kararınızdır. Kim neyin anlamlı neyin anlamlı olmadığını söyleyecek? Cevap sizsiniz, programcı. Saf matematiğin içinde, matrisler arasındaki çarpma temel aritmetikteki iki sayı arasındaki çarpma işleminden farklı bir şey ifade eder. Yine de her iki çarpma türü için de aynı sembolleri kullanıyoruz.
Mühendis

2
@paercebal: İddia maalesef doğru. Hareket halindeyken görmek için IOstreams'ten başka bir yere bakmanıza gerek yok. Neyse ki, çoğu geliştirici mevcut operatörler için yeni semantik icat etme konusunda daha ihtiyatlı.
Ben Voigt

5
@BenVoigt: [...] Ve addişlevin gerçekten yanlış kullanılabileceğinden bile bahsetmiyorum (çarpma yapmak veya muteks almak gibi) ... user14128 tarafından belirtilen kötüye kullanım işleçlerle sınırlı değildir, ancak Operatörün aşırı yüklenmesi ile ilgili olarak, C + 'nın önceki günlerinden geldiğine inandığım bir tür patolojik korku var, C +' nın, Java'ya değiştirilmemiş, ama neyse ki C # 'a gitmediği bir korku ... ve net işlevler / işleçler yazmak geliştiricinin görevidir. Dil değil.
paercebal

3
@ jbo5112: Örnek: cout << f() || g(); Parantezler netleştirmiyor, düzeltiyorlar. Ve bit kaydırma operatörleri istismar edilmiyordu, gerekli olmayacaklardı. Neden daha cout << (5&3) << endl;iyi cout.fmt(5&3)(endl);? İşlev çağırma operatörünü bir işlev üyesi değişkeni üzerinde kullanmak, yalnızca glif güzel göründüğünden, bitsel işleçleri yeniden konumlandırmaktan çok daha iyi bir tasarım olacaktır. Ancak bu, akışlarda yanlış olan tek şeyden çok uzaktır.
Ben Voigt

5

Operatörün aşırı yüklenmesi ile kendinizi gerçekten ayağınızdan vurabilirsiniz. İşaretçilerle insanlar onlarla aptalca hatalar yapıyorlar ve bu yüzden makasları almaya karar verdiler.

En azından sebebinin bu olduğunu düşünüyorum. Zaten senin tarafındayım. :)



2
Bu çok kötü bir düşünce tarzı. Kendinizi ayağınızdan vurabilirsiniz, ellerinizi kesmeyi tercih ederiz, böylece yapamazsınız. Ve tabii ki kendini vuracak bir aptal olduğunuzu varsayalım.
ntj

5

Bazı insanlar, Java'da aşırı yükleme yapan operatörün belirsizliğe yol açacağını söylüyor. Bu insanlar hiç BigDecimal kullanarak bir finansal değeri bir yüzde artırmak gibi bazı temel matematik yapıyor bazı Java kodu bakmak için durdu mu? .... böyle bir egzersizin ayrıntı düzeyi, kendi gizleme gösterisine dönüşür. İronik bir şekilde, Java'ya aşırı yükleme operatörü eklemek, bu matematiksel kodu zarif ve basit (daha az gizlenmiş) yapacak kendi Para Birimi sınıfımızı oluşturmamıza izin verecektir.


4

Operatörün aşırı yüklenmesinin, operatörün işlem mantığına uymadığı mantıksal hatalara yol açtığını söylemek, hiçbir şey söylememek gibi. İşlev adı işlem mantığı için uygun değilse aynı tür hatalar ortaya çıkar - o zaman çözüm nedir: işlev kullanma yeteneğini bırakın !? Bu komik bir cevap - "İşlem mantığı için uygun değil", her parametre adı, her sınıf, işlev veya mantıksal olarak uygunsuz olabilecek her şey. Bu seçeneğin saygın bir programlama dilinde mevcut olması gerektiğini ve bunun güvenli olmadığını düşünenler olduğunu düşünüyorum - hey, her ikisi de onu kullanmanız gerektiğini söylemiyor. C # 'ı alalım. İşaretçileri düşürdüler ama hey - 'güvenli olmayan kod' ifadesi var - kendi sorumluluğunuzda olduğu gibi program.


4

Teknik olarak, her programlama dilinde, tamsayı ve gerçek sayılar gibi farklı sayı türleriyle başa çıkabilen operatör aşırı yüklenmesi vardır. Açıklama: Aşırı yükleme terimi, bir işlev için sadece birkaç uygulamanın olduğu anlamına gelir. Çoğu programlama dilinde, operatör + için, biri tamsayılar, diğeri gerçekler için farklı uygulamalar sağlanır, buna operatör aşırı yüklenmesi denir.

Şimdi, birçok kişi Java'nın operatörleri + dizeler eklemek için operatöre aşırı yüklemesi olduğunu garip buluyor ve matematiksel bir bakış açısından bu gerçekten garip olurdu, ancak bir programlama dilinin geliştiricisinin bakış açısından bakıldığında, yerleşik operatör aşırı yüklemesi eklemede yanlış bir şey yok operatör için + diğer sınıflar için örneğin String. Ancak, çoğu kişi String için + için yerleşik aşırı yükleme eklediğinizde, geliştirici için de bu işlevselliği sağlamak genellikle iyi bir fikirdir.

Geliştiricinin karar vermesi için bırakıldığından, operatörün kodun aşırı yüklenmesine neden olduğu yanlışlığına tamamen katılmıyorum. Bu düşünmek için saf ve oldukça dürüst olmak gerekirse, yaşlanıyor.

Java 8'de operatör aşırı yüklemesi eklemek için +1.


Java'nın +string-ish'i birleştirmek için kullanımı IMHO oldukça iğrenç, /bütün ve kesirli bölümler için C ve FORTRAN'da aşırı yükleme . Pascal'ın birçok versiyonunda, herhangi bir sayısal tipte aritmetik işleçlerin kullanılması, işlenenlerin dökümüne sayısal olarak eşdeğer sonuçlar verir Real, ancak tam sayı olmayan sonuçlar , tamsayılara atanabilmeleri için Truncveya Roundonlardan önce verilmelidir.
supercat

2

Java'nın uygulama dili olarak kabul edilmesi a, b ve c'nin tümü null başlangıç ​​değerlerine sahip Complex türüne başvurular olacaktır. Ayrıca, Kompleksin belirtilen BigInteger ve benzeri değiştirilemez BigDecimal gibi değişmez olduğunu varsayarsak, b ve c eklenerek döndürülen Kompleks'e referans atarken ve bu referansı a ile karşılaştırmadan, aşağıdakileri kastettiğinizi düşünüyorum.

Değil:

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

çok daha basit:

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

2
Ben miyim? ;) Eşittir, hem atama hem de karşılaştırma anlamına gelebilir, ancak = her zaman atamadır ve == her zaman karşılaştırmadır. İsimler büyük hata kaynaklarını kendileri getirebilir.

1

Bazen operatörün aşırı yüklenmesi, arkadaş sınıfları ve çoklu kalıtım olması iyi olurdu.

Ancak yine de iyi bir karar olduğunu düşünüyorum. Java, operatör aşırı yüklenmesine sahip olsaydı, kaynak koduna bakmadan operatör anlamlarından asla emin olamazdık. Şu anda bu gerekli değil. Ve bence operatör aşırı yüklenmesi yerine yöntemleri kullanma örneğiniz de oldukça okunabilir. İşleri daha açık hale getirmek istiyorsanız, her zaman kıllı ifadelerin üzerine bir yorum ekleyebilirsiniz.

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

12
Tabii ki, başka bir yerde de belirtildiği gibi, ekleme işlevinin anlamından da asla emin olamazsınız.
Tutulma

Doğru, yine de en azından operatörlerimin sabit kodlanmış olduğunu bilmek rahatlatıcı. Tabii ki, özelliklere sahip olmak ve bunları makul bir şekilde kullanmak bizi sadece iyi yapar. Sorun şu ki, birisinin bunları makul bir şekilde kullanıp kullanmadığını bilmek zor. Ve mantıklı bir tanım üzerinde hemfikir olduğunuzu. :-)

1
Kodu netleştirmek için eklenen yorum, operatörün aşırı yüklenmesini destekleyen bir dilde kodun nasıl görüneceğidir. Ayrıca, yorumun operatörler açısından yazılması, operatörün aşırı yüklenmesine karşı olduğunuza inanmaktadır.
Aluan Haddad

0

Bu, izin vermemek için iyi bir neden değil, pratik bir neden:

İnsanlar her zaman sorumlu bir şekilde kullanmazlar. Bu örneğe Python kütüphanesi scapy'den bakın:

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

İşte açıklama:

/ Operatörü iki katman arasında bir kompozisyon operatörü olarak kullanılmıştır. Bunu yaparken, alt katmanın bir veya daha fazla varsayılan alanı üst katmana göre aşırı yüklenebilir. (Yine de istediğiniz değeri verebilirsiniz). Bir dize ham katman olarak kullanılabilir.


0

Java Operatörü Aşırı Yüklemesinin Yerel Desteğine Alternatifler

Java'da operatör aşırı yüklenmesi olmadığından, bakabileceğiniz bazı alternatifler şunlardır:

  1. Başka bir dil kullanın. Hem Groovy hem de Scala'da operatör aşırı yüklenmesi var ve Java tabanlı.
  2. Java'da operatörün aşırı yüklenmesini sağlayan bir eklenti olan java-oo kullanın . Platformdan bağımsız OLMADIĞINI unutmayın. Ayrıca, birçok sorunu vardır ve Java'nın en son sürümleriyle (yani Java 10) uyumlu değildir. ( Orijinal StackOverflow Kaynağı )
  3. Kullanım JNI'yı , Java Native Interface veya alternatifler. Bu, Java'da kullanmak için C veya C ++ (belki diğerleri?) Yöntemlerini yazmanıza olanak tanır. Tabii ki bu platformdan bağımsız DEĞİLDİR.

Birisi başkalarının farkındaysa, lütfen yorum yapın, ben de bu listeye ekleyeceğim.


0

Java dili operatörün aşırı yüklenmesini doğrudan desteklemese de, etkinleştirmek için herhangi bir Java projesinde Manifold derleyici eklentisini kullanabilirsiniz . Java 8 - 13'ü (geçerli Java sürümü) destekler ve IntelliJ IDEA'da tamamen desteklenir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.