なぜ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それは簡単です、単に精神的にaを挿入てください -欠けているartlclesは、その人がネイティブの英語話者でもプログラマーでもない(またはこの人のように両方である)のどちらかであるという致命的なプレゼントです:プログラマーが記事をドロップできる理由は、コメントを短くし、提供されたスペースに簡単に収まるようにします。そこから、彼らはそれに慣れます。私の問題はレイアウトにあり、どういうわけか私はいつもGoogle検索でそのサイトにアクセスしています。幸いなことに、読みにくいページを見事に再フォーマットするClearlyという素晴らしいchrome拡張機能があります。
ycomp 2015年

1
なぜ、OPが最初の回答を受け入れたのか、理由はわかりません。@ 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=参照コピーを実行するためab現在は同じ値を参照しています。その結果、オブジェクトはそれ自体と等しいため、比較により「等しい」が生成されます。

コピーと参照の違いは、オペレーターのオーバーロードの混乱を増すだけです。@Sebastianが述べたように、JavaとC#はどちらも値と参照の等価性を別々にoperator+処理する必要があります- 値とオブジェクトを処理する可能性がありますが、operator=参照を処理するためにすでに実装されています。

C ++では、一度に1種類の比較のみを処理する必要があるため、混乱が少なくなります。例えば、上のComplexoperator=およびoperator==両方の値に取り組んでいる-の値をコピーし、それぞれの値を比較します。


6
とても簡単です... Pythonのようにして、オーバーロードされた割り当てはありません。
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

225
この答えは質問にまったく答えません。あなたは単に等号のJavaの使用を期待しています。b + Cが新しいComplexを返した場合、a = b + cは完全に有効であり、読み取りははるかに簡単です。aをその場で変更したい場合でも、a.set(b + c)は読みやすく、特に算術が取るに足らない場合はa.set((a b + b c)/ 5)またはa = a.multiply(b).add(b.multiply(c))。divide(5)。あなたの選択..
BT

24
または、私はそう思う..場合によっては、あなたの選択ではない
BT

9
C ++では、式テンプレートは余分なコピーの問題を解決します。ほとんどすべての主要な算術ライブラリは、まさにこの理由でこの手法を使用しています。また、a = b + cはa.foo(b.bar(c))の構文糖であり、これは実際の質問の最初の観察であるため、これは質問に対処していません。
Kaz Dragon

18
これは尋ねられた質問に対する答えではありません。これは、JavaとC ++の特定の違いに関する推測です。
シェプリン2013年

804

オペレーターの過負荷について不満を言う投稿がたくさんあります。

「オペレーターのオーバーロード」の概念を明確にし、この概念に別の視点を提供する必要があると感じました。

コードを難読化しますか?

この議論は誤りです。

すべての言語で難読化が可能です...

演算子のオーバーロードを介してC ++の場合と同様に、関数またはメソッドを介してCまたはJavaのコードを難読化するのは簡単です。

// 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 ++演算子のオーバーロードが想定されているのと同じ理由で禁止する必要がありますか?

クラスの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 ++では、演算子のオーバーロードにより、各コンテナーは同じ方法でコンテンツにアクセスします。

自然な高度な型操作

以下の例では、MatrixGoogleで見つかった「Java Matrixオブジェクト」と「C ++ Matrixオブジェクト」の最初のリンクを使用して見つかったオブジェクトを使用しています。

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

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

そして、これは行列に限定されません。BigIntegerおよびBigDecimalC ++におけるそれらの等価物は、ビルトインタイプとして明らかとされているのに対し、ジャワのクラス、同じ混乱冗長性に苦しみます。

自然反復子:

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

OK、Javaでも使用MyString = "Hello " + 25 + " World" ;できます...しかし、少し待ってください:これは演算子のオーバーロードですよね?浮気じゃないですか???

:-D

一般的なコード?

同じ汎用コード変更オペランドは、組み込み/プリミティブ(Javaにインターフェースがない)、標準オブジェクト(適切なインターフェースを持つことができない)、およびユーザー定義オブジェクトの両方に使用できます。

たとえば、任意のタイプの2つの値の平均値を計算します。

// 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では、演算子の実際の動作は、そのオペランドに従って変化します。たとえば、2つの整数を追加することは、2つのdouble、または1つの整数と1つのdoubleを追加することとは異なります。ポインタ算術領域全体もあります(キャストを行わない場合、ポインタに整数を追加できますが、2つのポインタを追加することはできません...)。

Javaでは、ポインター算術はありませんが、+演算子なしで文字列連結が見つかったとしても、「演算子の過負荷は悪である」という信条の例外を正当化するのに十分とんでもないことになります。

C(歴史的理由)またはJava(個人的理由、以下を参照)コーダーとして、自分でコードを提供することはできません。

C ++では、演算子のオーバーロードはオプションではありません...

C ++では、組み込み型の演算子オーバーロードは不可能です(これは良いことです)が、ユーザー定義型はユーザー定義の演算子オーバーロードを持つことができます

すでに前に述べたように、C ++では、Javaとは逆に、組み込み型と比較した場合、ユーザー型は言語の二級市民とは見なされません。したがって、組み込み型に演算子がある場合、ユーザー型も演算子を持つことができるはずです。

真実はそれで、同様にtoString()clone()equals()メソッドは、Javaのためのもの(すなわち、準標準のような)、C ++演算子のオーバーロードは、それが本来のC演算子、または前述のJavaメソッドとして自然のようになることをC ++のそんなに一部です。

テンプレートプログラミングと組み合わせると、演算子のオーバーロードはよく知られた設計パターンになります。実際、オーバーロードされた演算子を使用せずに、独自のクラスに演算子をオーバーロードしない限り、STLをはるかに超えることはできません。

...しかしそれは悪用されるべきではない

オペレーターのオーバーロードは、オペレーターのセマンティクスを尊重するよう努めるべきです。+演算子で減算しないでください(「add関数で減算しない」、「cloneメソッドでがらくたを返す」など)。

キャストのオーバーロードはあいまいさにつながる可能性があるため、非常に危険です。したがって、これらは明確に定義されたケースのために予約する必要があります。あなたが本当に何をやっている知っている限り、あなたがネイティブオペレーターその短絡評価を失うことになるとして、これまで、それらをオーバーロードしていないとお楽しみください。&&||&&||

それで...わかりました...では、なぜJavaではそれができないのでしょうか?

ジェームズ・ゴズリングがそう言ったので:

C ++で乱用する人が多すぎるのを見たので、オペレーターのオーバーロードをかなり個人的な選択として省略しました。

ジェームズ・ゴズリング。出典:http : //www.gotw.ca/publications/c_family_interview.htm

上のGoslingのテキストと下のStroustrupのテキストを比較してください。

ので、多くのC ++の設計上の決定は、いくつかの特定の方法で物事を行う人々を強制するために、私の嫌いで自分のルーツを持っている[...]多くの場合、私は私が個人的に嫌わ機能を非合法化するように誘惑された、私はそうすることを控え、私は私が持っていたとは思いませんでした他人に私の見解を強制する権利

Bjarne Stroustrup。出典:C ++の設計と進化(1.3一般的な背景)

演算子のオーバーロードはJavaにメリットがありますか?

一部のオブジェクトは、演算子のオーバーロード(BigDecimal、複素数、行列、コンテナー、イテレーター、コンパレーター、パーサーなどの具体的な数値型)から大きなメリットを得ます。

C ++では、Stroustrupの謙虚さのために、この利点から利益を得ることができます。Javaでは、ゴスリングの個人的な選択のために、単純に頭を悩ませています。

Javaに追加できますか?

現在Javaでオペレーターのオーバーロードを追加しない理由は、内部政治、機能へのアレルギー、開発者への不信感(Javaチームを悩ませていると思われる妨害行為など)、以前のJVMとの互換性、正しい仕様書などを書く時間

だから、この機能を待って息を止めないでください...

しかし、彼らはそれをC#で行います!!!

うん...

これは2つの言語の唯一の違いではありませんが、これは私を楽しませるのに失敗しません。

どうやら、C#の人々は、「すべてのプリミティブはstructであり、structObjectから派生している」ので、最初に試してみました。

そして、彼らは他の言語それをします !!!

使用される定義済み演算子のオーバーロードに対するすべてのFUDにもかかわらず、ScalaDartPythonF#C#DAlgol 68SmalltalkGroovyPerl 6、C ++、RubyHaskellMATLABEiffelLuaClojureFortran 90SwiftAdaDelphi 2005 ...

非常に多くの言語があり、非常に多くの異なる(時には反対の)哲学がありますが、それでもそれらはすべてその点に同意します。

思考の糧...


50
これは素晴らしい答えです。私はそれに同意しませんが、それでも優れた答えです。悪いオーバーロードで起こりうる問題は、良いオーバーロードの値を超えていると思います。
ダグラスリーダー

69
@ダグラスリーダー:ありがとう!オペレーターのオーバーロードはOOPに似ています。最初にそれを学ぶときは、基本クラスと継承をどこにでも置くように(甘い皮肉、Java APIなど)、オーバーロードをどこにでも書きます。しかし、これはかなり速く通過し、それを悪用しない限りずっと可能性を高く評価します。私自身のC ++に関する10年以上の経験では、自分のコードと他のプログラマーのコードの両方で見た悪いオーバーロードの数は非常に少ないので、一方でそれらを数えることができると思います。そして、これはsprintf、strcat、memset、およびバッファオーバーランに関する全体的なバグの数よりもはるかに少ないです。
paercebal 2010年

11
@Douglas Leeder:別のSOの質問でそれについて議論した後、演算子のオーバーロードの「恋人」と「嫌い者」の間のギャップはおそらくコードのアプローチの違いによって引き起こされると思います:「嫌い者」はより「関数」です重要なことです」という意味です。つまり、関数は1つのことだけを実行することを期待しています。したがって、演算子は言語で設計されたとおりに機能する必要があります。「愛好家」とは「オブジェクトが動作する必要がある」という意味です。つまり、関数(および演算子)はパラメーターのタイプに応じて動作を変更することができます。
paercebal 2010年

103
壮大な答え。私が今まで読んだ中で最も資格のあるdebunksの一つ。
セバスチャンマッハ

7
@MaartenBodewes:私が上で書いたすべての例、そしてあなたが「開発者として、あなたはゴスリングの個人的な選択のためにあなたは台無しにされています」という煩わしさすべてを悩ませていますか?「あなたの開発者は愚かです、天才の人々があなたのためにあなたが何を必要とするかを決めさせてください」という見方を守って、あなた自身の答えを書いてください。この議論は目的を果たしません。
paercebal 2016

44

James Goslingは、Javaの設計を次のように説明しました。

「あるアパートから別のアパートに移動するときの移動には、この原則があります。興味深い実験は、アパートを梱包してすべてを箱に入れ、次のアパートに移動し、必要になるまで何も開梱しないことです。だから、あなたは最初の食事を作り直すと、箱から何かを取り出します。その後、1か月ほど経つと、それを使用して、実際に必要なものをほぼ把握し、残りの食事を取ります。もの-どれだけ好きか、どれだけクールかを忘れて、ただ捨てるだけです。それがあなたの人生を簡素化し、あらゆる種類の設計問題でその原則を使用できるのは驚くべきことです。 「かっこいいか、単に面白いから」

ここで引用コンテキストを読むことができます

基本的に演算子のオーバーロードは、ある種のポイント、通貨、または複素数をモデル化するクラスに最適です。しかしその後、あなたはすぐに例を使い果たし始めます。

もう1つの要因は、「&&」、「||」、キャスト演算子、そしてもちろん「new」などの演算子をオーバーロードする開発者によるC ++の機能の悪用でした。これと値渡しおよび例外との組み合わせによる複雑さについては、Exceptional C ++ブックで詳しく説明されています


6
「値渡しと例外を組み合わせた演算子のオーバーロードの複雑さ」のコード例を教えていただけませんか?数年言語をいじって、C ++に関するすべての効果的な/例外的な本を所有して読んだにもかかわらず、私はあなたが何を意味しているのか理解できません。
paercebal 2011年

60
ジェームズゴスリングで機能するものがすべての人に機能するとは限りません。彼は、「興味のある」梱包実験を「私が必要としない世界のすべてのものを捨てて、だれもその物を使うことができない」という意味に外挿するのは信じられないほど近視眼的です。彼は明らかに私が必要とするものや使用するものを知りません。
BT

49
@BT:この問題に関するStroustrupの見解と比較した場合、最も啓発されるのはGoslingの見解です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 2011

29
@Software Monkey:「C ++は広く悪評があり、Javaは広く好まれている」これはマーケティングの誇大宣伝です。C ++は単独で成長したのに対し、Java(および.NET)はブルドーザーのマーケティングから利益を得たことを思い出してください。「広く好まれている言語」では、Javaがサーバーアプリケーションに限定されているのに対し、「広く悪意のある」(おそらくJava開発者やマネージャーがコード生成のコストを下げたい)ため、C ++は非常に高い高性能サーバーから高性能ゲームへ?[...]
paercebal 2011

16
@Hassan:各言語にはハックがあり、Javaのジェネリックはその良い例の1つです。今、約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の関数パラメーターの「参照渡し」をエミュレートしてみてください。私はコードを見て、痛いほど笑いました。これは、Goslingが使用しなかったものです。したがって、Javaに恐ろしいハックが必要でしたが、C#とC ++の両方で、ゼロコストでネイティブに存在しています。
paercebal 2012年

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;

実際には「Energy = 4 J」が出力されますが、これは正しいです。


1
「正確にメンテナンスが複雑で、地球上のどこでこれがコードを難読化するのか?」
Mooing Duck

13

Javaの設計者たちは、オペレーターのオーバーロードは価値があるよりも問題であると判断しました。そのような単純な。

すべてのオブジェクト変数が実際に参照である言語では、演算子のオーバーロードは、少なくともC ++プログラマにとって、まったく非論理的であるという追加の危険を伴います。C#の==演算子のオーバーロードand Object.EqualsおよびObject.ReferenceEquals(またはそれが呼び出されたもの)と状況を比較します。


8

Groovyにはオペレーターのオーバーロードがあり、JVMで実行されます。パフォーマンスへの影響を気にしない場合(毎日小さくなります)。メソッド名に基づいて自動的に行われます。たとえば、「+」は「plus(argument)」メソッドを呼び出します。


4
演算子のオーバーロードを伴うすべての構文を多用する言語がその手法を使用することを望みます。彼らがなぜメソッドの命名とルックアップの特別なバージョンを発明しなければならないのか私には理解できませんでした。StroustrupはD&EC ++の代替案については触れていません。C#チームは、Linq構文(にwhere ...なる.Where(i => ... )で適切なアプローチを採用しました。算術演算子で同じことをしたとしたら、多くのことがより簡単で強力になります。Javaには白紙の状態の利点があり、これを正しく行うことができます(ただし、宗教的な理由から、おそらくそうなることは決してありません)。
Daniel Earwicker

@DanielEarwicker、私はしばしば、人々がどちらかの側の動機を本質的に「宗教的」であるとタグ付けするという複雑な意見の相違がある場合に言及しました。

@noah、視覚的に区別できるようにメソッド名に特別なタグが付いていれば、このような演算子のオーバーロードの限られたサブセットを使用できます。"+" OLを実装するための__plus()メソッドを定義し、キャストや配列の添え字などのオーバーロードを遠くから遠ざけるようなもの。私が共存したくないのは、C ++とC#がそれを実装するのに適していると考えた方法です。

2
答えではありません。VMでは多くの言語が実行されています。演算子のオーバーロードは、それ自体が言語を切り替える正当な理由ではありません。
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:これは不当な主張です。私は12年以上プロのC ++開発者であり、この問題に遭遇することはめったにありません。実際、C ++で見たバグと設計エラーのほとんどは、Cスタイルのコード(void *、キャストなど)にありました
paercebal

6
-1。割り当てるすべての変数は、算術演算子記号と同様に記号です。その変数に名前を付けるためにフレーズを使用するか、1つの単語または1つの文字を使用するかは、ユーザー(またはチーム)の決定です。誰が何を意味し、何を意味しないのか。答えはプログラマーであるあなたです。純粋な数学では、行列間の乗算は、基本的な計算における2つの数値間の乗算とは異なるものを意味します。しかし、両方のタイプの乗算に同じ記号を使用しています。
エンジニア、

2
@paercebal:アサーションは残念ながら正しいです。実際の動作を確認するには、IOストリームよりも遠くを見る必要があります。ありがたいことに、ほとんどの開発者は、既存のオペレーターの新しいセマンティクスを発明することにもっと慎重になっています。
Ben Voigt 2014

5
@BenVoigt:[...]そして、add関数が本当に誤用される可能性があること(乗算やmutexの取得など)についても触れていません... user14128によって言及されている乱用は、演算子に限定されていませんが、演算子のオーバーロードに関して、C対C ++の初期の段階から来ると思われる一種の病理学的恐怖があります。Javaに変更されずにそのまま入った恐怖ですが、ありがたいことに、C#には入っていませんでした...最後に、セマンティクスを尊重します明確な関数/演算子を書くことは開発者の仕事です。言語ではありません。
paercebal 14

3
@ jbo5112:例:cout << f() || g(); 括弧は、それを明確にするのではなく、正しいものにします。そして、ビットシフト演算子が悪用されていなかったので、それらは必要ありませんでした。なぜcout << (5&3) << endl;より良いですcout.fmt(5&3)(endl);か?ファンクターメンバー変数で関数呼び出し演算子を使用すると、グリフが見栄えが良いという理由だけで、ビットごとの演算子を転用するよりもストリームの設計が無限に良くなります。しかし、これはストリームの唯一の問題とはほど遠いものです。
Ben Voigt

5

まあ、あなたは本当にオペレーターのオーバーロードで足元を撃つことができます。ポインターを使って愚かな間違いをするようなもので、はさみを取り除くことになりました。

少なくともそれが理由だと思います。とにかく私はあなたの味方です。:)



2
それは非常に悪い考え方です。あなたは足で自分を撃つことができます、私たちはむしろあなたの手を切るので、あなたはそれをすることができません。そしてもちろん、私たちはあなたが自分を撃つバカだと思います。
ntj 2018年

5

Javaでのオペレーターのオーバーロードは難読化につながると言う人もいます。それらの人々は、BigDecimalを使用してパーセンテージで金銭的価値を増やすなどの基本的な計算を行うJavaコードを見るのをやめたことがありますか?....そのような演習の冗長性は、難読化の独自のデモンストレーションになります。皮肉なことに、Javaにオペレーターのオーバーロードを追加すると、独自のCurrencyクラスを作成できるようになり、そのような数学コードがエレガントでシンプルになります(難読化が少なくなります)。


4

演算子のオーバーロードが、その演算子が操作ロジックと一致しないタイプの論理エラーを引き起こすと言うと、それは何も言わないようなものです。関数名が操作ロジックに不適切な場合も、同じタイプのエラーが発生します。解決策は何ですか。関数の使用能力を落とします!?これはコミカルな答えです-「操作ロジックには不適切」、すべてのパラメーター名、すべてのクラス、関数など、論理的に不適切である可能性があるもの。このオプションは、立派なプログラミング言語で利用できるはずだと思いますし、安全ではないと思う人もいるでしょう。C#を見てみましょう。彼らはポインタを垂れ流しましたが、ちょっと-「安全でないコード」ステートメントがあります-自分のリスクで好きなようにプログラムしてください。


4

技術的には、整数や実数など、さまざまなタイプの数値を処理できるすべてのプログラミング言語に演算子のオーバーロードがあります。説明:オーバーロードという用語は、1つの関数に対して複数の実装が存在することを意味します。ほとんどのプログラミング言語では、演算子+、整数用、実数用のさまざまな実装が提供されています。これは演算子オーバーロードと呼ばれます。

さて、多くの人々は、Javaが演算子+に対して演算子のオーバーロードを行い、文字列を一緒に追加するのが奇妙であると感じています。数学的な観点からは、これは実際には奇妙ですが、プログラミング言語の開発者の観点から見ると、組み込みの演算子のオーバーロードを追加することに問題はありません。演算子+、他のクラス(例:String)。ただし、文字列の+に組み込みのオーバーロードを追加したら、開発者にもこの機能を提供することをお勧めします。

これは、オペレーターがコードをオーバーロードしてコードを難読化するという誤解に完全に同意しません。これは単純な考え方で、正直に言うと古くなっています。

Java 8で演算子のオーバーロードを追加するための+1


+文字列っぽいものを何でも連結するためのJavaの使用は非常に恐ろしいです/。CおよびFORTRANでの全体および部分的な除算のオーバーロードも同様です。パスカルの多くのバージョンでは、任意の数値型の算術演算子の使用はにオペランドをキャストすると数値的に同等の結果が得られますReal全体の数字ではないかもしれない結果を介して供給されなければならないものの、TruncまたはRound、彼らは整数に割り当てることができます前に。
スーパーキャット2015年

2

Javaを実装言語とすると、a、b、cはすべて、初期値がnullのComplex型への参照になります。また、Complexが前述のBigIntegerおよび同様の不変のBigDecimalとして不変であると仮定すると、bとcを追加して返されたComplexへの参照を割り当てており、この参照をaと比較していないため、次のことを意味すると思います。

ない:

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

よりもはるかに単純です:

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

2
私ですか?;)Equalsは両方とも代入または比較を意味しますが、=は常に代入であり、==は常に比較です。名前自体がエラーの大きな原因となる場合があります。

1

場合によっては、演算子のオーバーロード、フレンドクラス、および多重継承が便利です。

しかし、それでも良い決断だったと思います。Javaに演算子のオーバーロードがあったとしたら、ソースコードを調べない限り、演算子の意味を確信することはできません。現時点では必要ありません。また、演算子のオーバーロードの代わりにメソッドを使用する例も読みやすいと思います。物事をより明確にしたい場合は、常に毛深い声明の上にコメントを追加できます。

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

12
もちろん、他の場所で述べたように、add関数の意味を確信することはできません。
Eclipseの

確かに、少なくとも私のオペレーターはハードコーディングされていることを知っておくのは、やはり心地よいと思います。もちろん、機能を用意し、それらを賢く使用することは、私たちにとって良いことです。問題は、誰かが賢明にそれらを使用したかどうかを知るのが難しいことです。そして、あなたは賢明に定義に同意すること。:-)

1
コードを明確にするために追加されたコメントは、演算子のオーバーロードをサポートする言語でのコードの外観です。さらに、コメントが演算子の観点から書かれているという事実は、演算子のオーバーロードに対するあなたの反対を否定します。
Aluan Haddad

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

ここに説明があります:

/演算子は、2つのレイヤー間の合成演算子として使用されています。そうするとき、下位層は、上位層に従ってオーバーロードされた1つ以上のデフォルトフィールドを持つことができます。(あなたはまだあなたが望む値を与えることができます)。文字列をrawレイヤーとして使用できます。


0

Javaオペレーターオーバーロードのネイティブサポートの代替

Javaには演算子のオーバーロードがないため、次のような代替手段を検討できます。

  1. 別の言語を使用してください。GroovyScalaはどちらも演算子のオーバーロードがあり、Javaに基づいています。
  2. Javaでのオペレーターのオーバーロードを可能にするプラグインであるjava-ooを使用します。プラットフォームに依存しないことに注意してください。また、多くの問題があり、Javaの最新リリース(つまりJava 10)と互換性がありません。(元のStackOverflowソース
  3. JNI、Java Native Interface、または代替手段を使用してください。これにより、Javaで使用するためのCまたはC ++(おそらく他のメソッド?)メソッドを記述できます。もちろん、これもプラットフォームに依存しません。

誰かが他の人を知っている場合はコメントしてください。私はそれをこのリストに追加します。


0

Java言語は演算子のオーバーロードを直接サポートしていませんが、任意のJavaプロジェクトでManifoldコンパイラプラグインを使用して有効にすることができます。Java 8-13(現在のJavaバージョン)をサポートし、IntelliJ IDEAで完全にサポートされています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.