java - method - operator overwrite c++



為什麼Java不提供運算符重載? (10)

James Gosling將Java設計為以下內容:

“當你從一間公寓搬到另一間公寓時,有一個關於搬家的原則,一個有趣的實驗是收拾你的公寓,把所有東西都放進箱子裡,然後搬進下一間公寓,在你需要的時候不要打開任何東西,重新做你的第一頓飯,然後你從盒子裡拿出東西,然後在一個月左右的時間裡,你已經用它來很清楚你生活中真正需要什麼東西,然後把剩下的東西 - 忘記你有多喜歡它,或者它有多酷 - 然後你就把它扔掉了,這簡直令人驚訝,它簡化了你的生活,你可以在各種設計問題中使用這個原則:不要僅僅因為它們很酷,或者只是因為它們很有趣。“

你可以http://www.gotw.ca/publications/c_family_interview.htm閱讀http://www.gotw.ca/publications/c_family_interview.htmhttp://www.gotw.ca/publications/c_family_interview.htm

基本上,運算符重載對模擬某種點,貨幣或複數的類非常有用。 但在此之後,您將很快開始耗盡示例。

另一個因素是開發人員濫用C ++中的功能,如'&&','||',演員操作員和當然'新'等。 Exceptional C ++書中詳細介紹了將此與傳值和異常相結合導致的複雜性。

從C ++到Java,一個明顯的未解決的問題是為什麼Java沒有包含運算符重載?

不是Complex a, b, c; a = b + c; Complex a, b, c; a = b + c;Complex a, b, c; a = b.add(c);簡單得多Complex a, b, c; a = b.add(c); Complex a, b, c; a = b.add(c);

是否有一個已知的原因,這是有效的論點,不允許運算符重載? 原因是任意的,還是失去時間?


Answer #1

Java設計人員認為運算符超載比其值得的更麻煩。 就那麼簡單。

在一種語言中,每一個對像變量實際上都是一個引用,但是對於一個C ++程序員來說,操作符重載會帶來更多的不合邏輯的危險。 將情況與C#的==運算符重載以及Object.EqualsObject.ReferenceEquals (或任何它所稱的)進行比較。


Answer #2

Assuming Java as the implementation language then a, b, and c would all be references to type Complex with initial values of null. Also assuming that Complex is immutable as the mentioned BigInteger and similar immutable BigDecimal , I'd I think you mean the following, as you're assigning the reference to the Complex returned from adding b and c, and not comparing this reference to a.

Isn't :

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

much simpler than:

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

Answer #3

Sometimes it would be nice to have operator overloading, friend classes and multiple inheritance.

However I still think it was a good decision. If Java would have had operator overloading then we could never be sure of operator meanings without looking through source code. At present that's not necessary. And I think your example of using methods instead of operator overloading is also quite readable. If you want to make things more clear you could always add a comment above hairy statements.

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

Answer #4

假設你想覆蓋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) );

在C ++中,這會復制該值,所以比較結果不會相等。 在Java中, operator=執行引用複制,所以ab現在引用相同的值。 因此,比較會產生“相等”,因為對象將與自身相等。

副本和引用之間的差異只會增加運算符重載的混淆。 正如@Sebastian所說,Java和C#都必須分別處理值和引用的相等性 - operator+可能會處理值和對象,但operator=已經實現來處理引用。

在C ++中,你只應該一次處理一種比較,所以它可能不那麼令人困惑。 例如,在Complexoperator=operator==都處理值 - 分別複製值和比較值。


Answer #5

從技術上講,每種編程語言都有操作符重載,可以處理不同類型的數字,例如整數和實數。 說明:術語超載意味著一個函數只有幾個實現。 在大多數編程語言中,為運算符+提供了不同的實現,一個用於整數,一個用於實數,這稱為運算符重載。

現在,很多人發現Java有操作符重載操作符+以將字符串添加到一起,並且從數學的角度來看,這確實很奇怪,但是從編程語言的開發者的角度來看,添加內置操作符重載沒有任何問題對於運營商+其他類如String。 然而,大多數人都同意,一旦為String增加了內置重載+,那麼為開發人員提供這個功能通常也是一個好主意。

完全不同意運算符重載混淆代碼的謬誤,因為這是由開發人員決定的。 這是天真的想法,而且很老實,它變老了。

用於在Java 8中添加運算符重載的+1。


Answer #6

有人說Java中的運算符重載會導致混淆。 那些人有沒有停下來看看一些Java代碼做一些基本的數學運算,比如使用BigDecimal以百分比增加財務價值? ......這樣一種練習的冗長變成了自己對混淆的表現。 Ironically, adding operator overloading to Java would allow us to create our own Currency class which would make such mathematical code elegant and simple (less obsfuscated).


Answer #7

有很多帖子抱怨運營商超載。

我覺得我不得不澄清“運營商超載”的概念,為這個概念提供了另一種觀點。

代碼混淆?

這個論點是一個謬論。

所有語言都可以進行混淆...

通過函數/方法在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的標準接口中

再舉一個例子,我們來看看Java中的Cloneable接口

你應該克隆實現這個接口的對象。 但你可以說謊。 並創建一個不同的對象。 事實上,這個接口非常弱,你可以完全返回另一種類型的對象,只是為了它的樂趣:

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

由於Cloneable接口可能被濫用/混淆,應該禁止使用相同的理由C ++運算符重載應該是什麼?

我們可以重載MyComplexNumber類的toString()方法,使其返回一天中字符串化的小時。 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

這不僅限於矩陣。 Java的BigIntegerBigDecimal類遭受相同的混淆冗長,而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 : http://www.gotw.ca/publications/c_family_interview.htm

請將上面的Gosling文本與Stroustrup的以下內容進行比較:

許多C ++設計決策的根源在於我不喜歡強迫人們以某種特定的方式來做事[...]通常,我被誘惑取締我個人不喜歡的功能,我沒有這樣做,因為我認為我沒有有權強制我對他人的看法

Bjarne Stroustrup。 來源:C ++的設計和發展(1.3一般背景)

運算符重載會使Java受益嗎?

一些對象將從運算符重載(具體或數字類型,如BigDecimal,複數,矩陣,容器,迭代器,比較器,解析器等)中受益匪淺。

在C ++中,由於Stroustrup的謙遜,您可以從中受益。 在Java中,由於Gosling的個人選擇 ,你只是被搞砸了。

它可以被添加到Java?

現在用Java不添加運算符重載的原因可能是內部政治,對特性的過敏,對開發人員的不信任(你知道,似乎困擾Java團隊的破壞者......),與以前的JVM的兼容性,時間寫出正確的規格等。

所以不要屏住呼吸等待這個功能...

但他們在C#中做到了!

是啊...

雖然這遠不是兩種語言之間的唯一區別,但這個永遠不會令我感到厭倦。

顯然,C#人員,他們的“每個原語是一個struct ,並且一個struct派生自Object” ,在第一次嘗試時就做對了。

他們用其他語言來做!

儘管所有FUD都反對使用定義的運算符重載,但下列語言支持它: Scala , DartPythonF#C#DAlgol 68SmalltalkGroovyPerl 6 ,C ++, Ruby , HaskellMATLABEiffelLua , Clojure , Fortran 90SwiftAdaDelphi 2005 ...

很多語言有很多不同的(有時是相反的)哲學,但他們都同意這一點。

食物的思想...


Answer #8

說操作符重載會導致邏輯錯誤,該操作符與操作邏輯不匹配,這就像什麼都沒說。 如果函數名不適合操作邏輯,那麼會出現相同類型的錯誤 - 那麼解決方案是什麼:降低函數使用的能力!? 這是一個滑稽的回答 - “不適合操作邏輯”,每個參數名稱,每個類別,函數或任何可能在邏輯上都不合適的答案。 我認為這個選項應該以可敬的編程語言提供,那些認為它是不安全的 - 嘿,沒有兩個人說你必須使用它。 讓我們拿C#。 他們貶低了指針,但嘿 - 有'不安全的代碼'聲明 - 根據您自己的風險進行編程。


Answer #9

那麼你真的可以在操作員超載的情況下在自己的腳下開槍自殺。 就像人們用指針一樣,他們犯了愚蠢的錯誤,所以決定拿走剪刀。

至少我認為這是原因。 無論如何,我在你身邊。 :)





operator-overloading