Ada banyak postingan yang mengeluhkan kelebihan operator.
Saya merasa saya harus mengklarifikasi konsep "operator overloading", menawarkan sudut pandang alternatif pada konsep ini.
Kode membingungkan?
Argumen ini salah.
Kebingungan dimungkinkan dalam semua bahasa ...
Sangat mudah untuk mengaburkan kode dalam C atau Java melalui fungsi / metode seperti di C ++ melalui overload operator:
// 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 ;
}
... Bahkan di antarmuka standar Java
Sebagai contoh lain, mari kita lihat Cloneable
antarmuka di Jawa:
Anda seharusnya mengkloning objek yang mengimplementasikan antarmuka ini. Tapi kamu bisa berbohong. Dan buat objek yang berbeda. Faktanya, antarmuka ini sangat lemah sehingga Anda bisa mengembalikan jenis objek lain secara bersamaan, hanya untuk bersenang-senang:
class MySincereHandShake implements Cloneable
{
public Object clone()
{
return new MyVengefulKickInYourHead() ;
}
}
Karena Cloneable
antarmuka dapat disalahgunakan / dikaburkan, haruskah itu dilarang dengan alasan yang sama C ++ operator overload seharusnya?
Kita bisa membebani toString()
metode suatu MyComplexNumber
kelas untuk mengembalikannya pada jam yang ditentukan hari itu. Haruskah toString()
kelebihan beban juga dilarang? Kita bisa menyabotase MyComplexNumber.equals
agar mengembalikan nilai acak, memodifikasi operan ... dll dll. Dll.
Di Jawa, seperti dalam C ++, atau bahasa apa pun, programmer harus menghormati semantik minimum saat menulis kode. Ini berarti menerapkan add
fungsi yang menambah, dan Cloneable
metode implementasi yang mengkloning, dan ++
operator daripada penambahan.
Apa sih yang membingungkan?
Sekarang kita tahu bahwa kode dapat disabotase bahkan melalui metode Java yang asli, kita dapat bertanya pada diri sendiri tentang penggunaan nyata dari operator yang berlebihan di C ++?
Notasi yang jelas dan alami: metode vs. kelebihan operator?
Kami akan membandingkan di bawah ini, untuk kasus yang berbeda, kode "sama" di Java dan C ++, untuk memiliki gagasan tentang jenis gaya pengkodean yang lebih jelas.
Perbandingan alami:
// 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 ;
Harap dicatat bahwa A dan B dapat berupa jenis apa pun dalam C ++, selama operator kelebihan beban disediakan. Di Jawa, ketika A dan B bukan primitif, kode dapat menjadi sangat membingungkan, bahkan untuk objek seperti primitif (BigInteger, dll.) ...
Pengakses dan larik array / wadah alami:
// 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
Di Jawa, kami melihat bahwa untuk setiap wadah melakukan hal yang sama (mengakses kontennya melalui indeks atau pengidentifikasi), kami memiliki cara berbeda untuk melakukannya, yang membingungkan.
Di C ++, setiap kontainer menggunakan cara yang sama untuk mengakses kontennya, terima kasih kepada operator yang berlebihan.
Manipulasi tipe lanjutan alami
Contoh di bawah ini menggunakan Matrix
objek, ditemukan menggunakan tautan pertama yang ditemukan di Google untuk "objek Java Matrix " dan "objek 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
Dan ini tidak terbatas pada matriks. The BigInteger
dan BigDecimal
kelas Java menderita bertele-tele membingungkan yang sama, sedangkan setara dalam C ++ adalah sebagai jelas seperti built-in jenis.
Iterator alami:
// 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
Functors alami:
// C++ Functors
myFunctorObject("Hello World", 42) ;
// Java Functors ???
myFunctorObject.execute("Hello World", 42) ;
Rangkaian teks:
// 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") ;
Ok, di Jawa Anda bisa menggunakan MyString = "Hello " + 25 + " World" ;
juga ... Tapi, tunggu sebentar: Ini overloading operator, bukan? Bukankah itu curang ???
:-D
Kode generik?
Operan pemodifikasi kode generik yang sama harus dapat digunakan baik untuk built-in / primitif (yang tidak memiliki antarmuka di Jawa), objek standar (yang tidak dapat memiliki antarmuka yang tepat), dan objek yang ditentukan pengguna.
Misalnya, menghitung nilai rata-rata dari dua nilai tipe arbitrer:
// 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.
Membahas kelebihan operator
Sekarang kita telah melihat perbandingan yang adil antara kode C ++ menggunakan operator overloading, dan kode yang sama di Jawa, kita sekarang dapat membahas "operator overloading" sebagai sebuah konsep.
Kelebihan operator sudah ada sejak sebelum komputer
Bahkan di luar ilmu komputer, ada operator overloading: Misalnya, dalam matematika, operator seperti +
, -
, *
, dll kelebihan beban.
Memang, signifikansi +
, -
, *
, dll perubahan tergantung pada jenis dari operan (numeric, vektor, fungsi gelombang kuantum, matriks, dll).
Sebagian besar dari kita, sebagai bagian dari program sains kami, belajar banyak signifikansi untuk operator, tergantung pada jenis operan. Apakah kita mendapati mereka membingungkan, mereka?
Kelebihan operator tergantung pada operannya
Ini adalah bagian terpenting dari kelebihan operator: Seperti dalam matematika, atau dalam fisika, operasi tergantung pada jenis operannya.
Jadi, ketahuilah jenis operan, dan Anda akan tahu efek operasinya.
Bahkan C dan Java memiliki operator (hard-code) overloading
Dalam C, perilaku nyata dari operator akan berubah sesuai dengan operannya. Misalnya, menambahkan dua bilangan bulat berbeda dari menambahkan dua ganda, atau bahkan satu bilangan bulat dan satu ganda. Bahkan ada domain aritmatika pointer keseluruhan (tanpa casting, Anda dapat menambahkan pointer ke integer, tetapi Anda tidak dapat menambahkan dua pointer ...).
Di Jawa, tidak ada pointer aritmatika, tetapi seseorang masih menemukan rangkaian string tanpa +
operator akan cukup konyol untuk membenarkan pengecualian dalam kredo "operator kelebihan muatan jahat".
Hanya saja Anda, sebagai kode C (karena alasan historis) atau Java (karena alasan pribadi , lihat di bawah), Anda tidak dapat menyediakan kode Anda sendiri.
Di C ++, kelebihan operator bukan opsional ...
Dalam C ++, overloading operator untuk tipe bawaan tidak dimungkinkan (dan ini adalah hal yang baik), tetapi tipe yang ditentukan pengguna dapat memiliki overload operator yang ditentukan pengguna .
Seperti yang sudah dikatakan sebelumnya, dalam C ++, dan sebaliknya ke Java, tipe pengguna tidak dianggap sebagai warga kelas dua bahasa, jika dibandingkan dengan tipe bawaan. Jadi, jika tipe bawaan memiliki operator, tipe pengguna harus dapat memilikinya juga.
Yang benar adalah bahwa, seperti toString()
, clone()
, equals()
metode adalah untuk Java ( yaitu kuasi-standar-seperti ), C ++ operator overloading begitu banyak bagian dari C ++ sehingga menjadi sebagai alam sebagai operator C yang asli, atau sebelum metode Java disebutkan.
Dikombinasikan dengan pemrograman template, overloading operator menjadi pola desain yang terkenal. Faktanya, Anda tidak bisa melangkah terlalu jauh di STL tanpa menggunakan operator yang kelebihan beban, dan operator yang kelebihan beban untuk kelas Anda sendiri.
... tapi jangan disalahgunakan
Overloading operator harus berusaha untuk menghormati semantik operator. Jangan kurangi dalam +
operator (seperti dalam "jangan kurangi dalam suatu add
fungsi", atau "kembalikan omong kosong dalam suatu clone
metode").
Kelebihan beban pemain bisa sangat berbahaya karena dapat menyebabkan ambiguitas. Jadi mereka harus benar-benar dicadangkan untuk kasus-kasus yang terdefinisi dengan baik. Adapun &&
dan ||
, tidak pernah membebani mereka kecuali jika Anda benar-benar tahu apa yang Anda lakukan, karena Anda akan kehilangan evaluasi hubung singkat bahwa operator asli &&
dan ||
menikmati.
Jadi ... Ok ... Lalu mengapa tidak mungkin di Jawa?
Karena James Gosling berkata demikian:
Saya membiarkan operator kelebihan beban sebagai pilihan yang cukup pribadi karena saya telah melihat terlalu banyak orang menyalahgunakannya di C ++.
James Gosling. Sumber: http://www.gotw.ca/publications/c_family_interview.htm
Silakan bandingkan teks Gosling di atas dengan Stroustrup di bawah ini:
Banyak keputusan desain C ++ berakar pada ketidaksukaan saya karena memaksa orang untuk melakukan sesuatu dengan cara tertentu [...] Sering kali, saya tergoda untuk melarang fitur yang saya pribadi tidak suka, saya menahan diri untuk tidak melakukannya karena saya tidak berpikir saya punya hak untuk memaksakan pandangan saya pada orang lain .
Bjarne Stroustrup. Sumber: Desain dan Evolusi C ++ (1.3 Latar Belakang Umum)
Apakah kelebihan beban operator menguntungkan Java?
Beberapa objek akan sangat diuntungkan dari kelebihan operator (jenis beton atau numerik, seperti BigDecimal, bilangan kompleks, matriks, wadah, iterator, pembanding, pengurai, dll.).
Dalam C ++, Anda bisa mendapat untung dari manfaat ini karena kerendahan hati Stroustrup. Di Jawa, Anda benar-benar kacau karena pilihan pribadi Gosling .
Bisakah itu ditambahkan ke Jawa?
Alasan untuk tidak menambah kelebihan operator sekarang di Jawa bisa menjadi perpaduan politik internal, alergi terhadap fitur, ketidakpercayaan pengembang (Anda tahu, penyabot yang tampaknya menghantui tim Java ...), kompatibilitas dengan JVM sebelumnya, waktu untuk menulis spesifikasi yang benar, dll.
Jadi jangan menahan nafas menunggu fitur ini ...
Tetapi mereka melakukannya dalam C # !!!
Ya...
Meskipun ini bukan satu-satunya perbedaan antara kedua bahasa, yang satu ini tidak pernah gagal untuk menghibur saya.
Rupanya, orang-orang C #, dengan "setiap primitif adalah a struct
, dan struct
berasal dari Object" , melakukannya dengan benar pada percobaan pertama.
Dan mereka melakukannya dalam bahasa lain !!!
Terlepas dari semua FUD terhadap kelebihan operator yang didefinisikan, bahasa berikut mendukungnya: 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 ...
Begitu banyak bahasa, dengan begitu banyak filsafat yang berbeda (dan terkadang bertentangan), namun mereka semua sepakat tentang hal itu.
Bahan untuk dipikirkan...