Tại sao Java không cung cấp quá tải toán tử?


406

Đến từ C ++ sang Java, câu hỏi chưa được trả lời rõ ràng là tại sao Java không bao gồm quá tải toán tử?

Không Complex a, b, c; a = b + c;đơn giản hơn nhiều Complex a, b, c; a = b.add(c);?

Có một lý do được biết đến cho điều này, các đối số hợp lệ cho việc không cho phép toán tử quá tải? Là lý do tùy tiện, hoặc mất thời gian?



1
@zzzz, tôi có một thời gian khó đọc bài viết đó. Đây có phải là tự động dịch, hoặc tiếng Anh là ngôn ngữ thứ 2 của nhà văn? Tôi thấy các cuộc thảo luận ở đây sẽ sạch sẽ hơn nhiều.

25
Đối với những người đóng cửa vì điều này không mang tính xây dựng, câu hỏi này đã mang lại một số hộp thoại mang tính xây dựng nhất mà tôi đã thấy tại SO. Có lẽ đó là một ứng cử viên tốt hơn cho các lập trình viên.stackexchange.com , nhưng có những lúc tôi nghĩ rằng SO đang bị loại bỏ quá mức đối với các chủ đề rộng hơn.

@ KhôngNaMe thật dễ dàng, chỉ cần chèn một cách tinh thần và các nghệ sĩ bị mất tích là một sự cho rằng người đó không phải là người nói tiếng Anh bản địa hoặc lập trình viên (hoặc như anh chàng này, cả hai :) Lý do lập trình viên có thể bỏ bài viết là nó có thể làm cho các bình luận ngắn hơn và phù hợp dễ dàng hơn trong không gian được cung cấp .. từ đó, họ chỉ quen với nó. Vấn đề của tôi là về bố cục, bằng cách nào đó tôi luôn nhấn trang web đó trong các tìm kiếm google. May mắn thay, có một phần mở rộng chrome tuyệt vời được gọi là Rõ ràng mà định dạng lại khó đọc các trang tuyệt vời.
ycomp

1
Tôi không thấy lý do tại sao & làm thế nào OP chấp nhận câu trả lời đầu tiên? Câu trả lời được viết bởi @ stackoverflow.com/users/14089/paercebal là tuyệt vời. Nó nên được chấp nhận.
Kẻ hủy diệt

Câu trả lời:


13

Giả sử bạn muốn ghi đè giá trị trước đó của đối tượng được gọi bởi a, thì một hàm thành viên sẽ phải được gọi.

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

Trong C ++, biểu thức này báo cho trình biên dịch tạo ba (3) đối tượng trên ngăn xếp, thực hiện phép cộng và sao chép giá trị kết quả từ đối tượng tạm thời vào đối tượng hiện có a.

Tuy nhiên, trong Java, operator=không thực hiện sao chép giá trị cho các loại tham chiếu và người dùng chỉ có thể tạo các loại tham chiếu mới, không phải các loại giá trị. Vì vậy, đối với loại do người dùng xác định có tên Complex, gán có nghĩa là sao chép tham chiếu đến giá trị hiện có.

Thay vào đó hãy xem xét:

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

Trong C ++, điều này sao chép giá trị, do đó so sánh sẽ cho kết quả không bằng nhau. Trong Java, operator=thực hiện sao chép tham chiếu, vì vậy abhiện đang đề cập đến cùng một giá trị. Kết quả là, so sánh sẽ tạo ra "bằng", vì đối tượng sẽ so sánh bằng chính nó.

Sự khác biệt giữa các bản sao và tài liệu tham khảo chỉ làm tăng thêm sự nhầm lẫn của quá tải toán tử. Như @Sebastian đã đề cập, cả Java và C # đều phải xử lý riêng biệt giá trị và tham chiếu tham chiếu - operator+có thể sẽ xử lý các giá trị và đối tượng, nhưng operator=đã được triển khai để xử lý các tham chiếu.

Trong C ++, bạn chỉ nên xử lý một loại so sánh tại một thời điểm, vì vậy nó có thể ít gây nhầm lẫn hơn. Ví dụ, trên Complex, operator=operator==cả hai đều làm việc trên các giá trị - sao chép các giá trị và so sánh các giá trị tương ứng.


6
Nó thực sự khá đơn giản ... Chỉ cần làm như Python và không có nhiệm vụ quá tải.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳

225
Câu trả lời này không trả lời câu hỏi nào cả. Bạn chỉ đơn giản là sử dụng dấu bằng của java. Nếu b + C trả về một Complex mới, thì a = b + c sẽ hoàn toàn hợp lệ và có thể đọc đơn giản hơn nhiều. Ngay cả khi bạn muốn sửa đổi một vị trí, a.set (b + c) đơn giản hơn rất nhiều để đọc - đặc biệt là khi số học là bất kỳ nhiều hơn tầm thường: a.set ((a b + b c) / 5) hoặc a = a.multiply (b) .add (b.multiply (c)). chia (5). Sự lựa chọn của bạn ..
BT

24
Hoặc tôi đoán .. không phải sự lựa chọn của bạn, như trường hợp có thể
BT

9
Trong C ++, Mẫu biểu thức giải quyết vấn đề của bản sao thêm. Khá nhiều tất cả các thư viện số học chính sử dụng kỹ thuật này vì lý do này. Ngoài ra, điều này không giải quyết được câu hỏi, vì a = b + c chỉ là đường cú pháp cho a.foo (b.bar (c)), đây thực sự là quan sát ban đầu trong câu hỏi.
Kaz Dragon

18
Đây không phải là câu trả lời cho câu hỏi. Đây là suy đoán của ai đó về sự khác biệt nhất định giữa Java và C ++.
SChepurin

804

Có rất nhiều bài viết phàn nàn về quá tải nhà điều hành.

Tôi cảm thấy mình phải làm rõ các khái niệm "quá tải toán tử", đưa ra một quan điểm thay thế cho khái niệm này.

Mã che giấu?

Lập luận này là một ngụy biện.

Obfuscating là có thể trong tất cả các ngôn ngữ ...

Thật dễ dàng để làm xáo trộn mã trong C hoặc Java thông qua các hàm / phương thức như trong C ++ thông qua quá tải toán tử:

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

... Ngay cả trong các giao diện chuẩn của Java

Đối với một ví dụ khác, chúng ta hãy xem Cloneablegiao diện trong Java:

Bạn có nghĩa vụ sao chép đối tượng thực hiện giao diện này. Nhưng bạn có thể nói dối. Và tạo ra một đối tượng khác nhau. Trên thực tế, giao diện này yếu đến mức bạn có thể trả lại một loại đối tượng khác hoàn toàn, chỉ để cho vui:

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

Cloneablegiao diện có thể bị lạm dụng / bị xáo trộn, nên nó có bị cấm trên cùng một lý do quá tải toán tử C ++ không?

Chúng ta có thể quá tải toString()phương thức của một MyComplexNumberlớp để nó trả về giờ được xâu chuỗi trong ngày. Nên toString()quá tải bị cấm, quá? Chúng ta có thể phá hoại MyComplexNumber.equalsđể nó trả về một giá trị ngẫu nhiên, sửa đổi các toán hạng ... vv, v.v.

Trong Java, như trong C ++, hoặc bất kỳ ngôn ngữ nào, lập trình viên phải tôn trọng tối thiểu ngữ nghĩa khi viết mã. Điều này có nghĩa là thực hiện một addchức năng bổ sung và Cloneablephương thức thực hiện nhân bản và ++toán tử hơn số gia.

Cái gì đang xáo trộn?

Bây giờ chúng ta biết rằng mã có thể bị phá hoại ngay cả thông qua các phương thức Java nguyên sơ, chúng ta có thể tự hỏi về việc sử dụng thực sự của quá tải toán tử trong C ++ không?

Ký hiệu rõ ràng và tự nhiên: phương pháp so với quá tải toán tử?

Chúng ta sẽ so sánh bên dưới, đối với các trường hợp khác nhau, mã "giống nhau" trong Java và C ++, để có ý tưởng về kiểu mã hóa nào rõ ràng hơn.

So sánh tự nhiên:

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

Xin lưu ý rằng A và B có thể thuộc bất kỳ loại nào trong C ++, miễn là quá tải toán tử được cung cấp. Trong Java, khi A và B không phải là nguyên thủy, mã có thể trở nên rất khó hiểu, ngay cả đối với các đối tượng giống như nguyên thủy (BigInteger, v.v.) ...

Bộ truy cập mảng / container tự nhiên và đăng ký:

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

Trong Java, chúng ta thấy rằng đối với mỗi container để làm điều tương tự (truy cập nội dung của nó thông qua một chỉ mục hoặc mã định danh), chúng ta có một cách khác nhau để làm điều đó, điều này gây nhầm lẫn.

Trong C ++, mỗi container sử dụng cùng một cách để truy cập nội dung của nó, nhờ quá tải toán tử.

Thao tác tự nhiên tiên tiến

Các ví dụ bên dưới sử dụng một Matrixđối tượng, được tìm thấy bằng cách sử dụng các liên kết đầu tiên được tìm thấy trên Google cho " đối tượng ma trận Java " và " đối tượng ma trận 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

Và điều này không giới hạn trong ma trận. Các lớp BigIntegerBigDecimalJava phải chịu cùng một mức độ khó hiểu, trong khi các tương đương của chúng trong C ++ rõ ràng như các kiểu dựng sẵn.

Lặp đi lặp lại tự nhiên:

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

Functor tự nhiên:

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

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

Nối văn bản:

// 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, trong Java bạn cũng có thể sử dụng MyString = "Hello " + 25 + " World" ;... Nhưng, đợi một chút: Đây là quá tải toán tử, phải không? Không phải là gian lận sao?

: -D

Mã chung?

Các toán hạng sửa đổi mã chung giống nhau phải có thể sử dụng được cho cả các hàm dựng sẵn / nguyên thủy (không có giao diện trong Java), các đối tượng tiêu chuẩn (không thể có giao diện phù hợp) và các đối tượng do người dùng định nghĩa.

Ví dụ: tính giá trị trung bình của hai giá trị của các loại tùy ý:

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

Thảo luận về quá tải toán tử

Bây giờ chúng ta đã thấy các so sánh công bằng giữa mã C ++ khi sử dụng nạp chồng toán tử và cùng mã trong Java, bây giờ chúng ta có thể thảo luận về "quá tải toán tử" như là một khái niệm.

Quá tải toán tử tồn tại từ trước khi máy tính

Thậm chí bên ngoài của khoa học máy tính, có nhà điều hành quá tải: Ví dụ, trong toán học, các nhà khai thác thích +, -, *, vv được quá tải.

Thật vậy, ý nghĩa của +, -, *vv thay đổi tùy thuộc vào loại của toán hạng (numerics, vectơ, hàm sóng lượng tử, ma trận, vv).

Hầu hết chúng ta, như một phần của các khóa học khoa học, đã học được nhiều ý nghĩa cho các toán tử, tùy thuộc vào các loại toán hạng. Chúng tôi đã tìm thấy chúng khó hiểu, họ?

Quá tải toán tử phụ thuộc vào toán hạng của nó

Đây là phần quan trọng nhất của quá tải toán tử: Giống như trong toán học hoặc trong vật lý, phép toán phụ thuộc vào kiểu toán hạng của nó.

Vì vậy, biết loại toán hạng, và bạn sẽ biết hiệu quả của hoạt động.

Ngay cả C và Java cũng có quá tải toán tử (mã hóa cứng)

Trong C, hành vi thực của toán tử sẽ thay đổi theo toán hạng của nó. Ví dụ: thêm hai số nguyên khác với việc thêm hai số nhân, hoặc thậm chí một số nguyên và một số nguyên. Thậm chí còn có toàn bộ miền số học con trỏ (không cần truyền, bạn có thể thêm vào con trỏ một số nguyên, nhưng bạn không thể thêm hai con trỏ ...).

Trong Java, không có số học con trỏ, nhưng ai đó vẫn tìm thấy nối chuỗi mà không có +toán tử sẽ đủ lố bịch để biện minh cho một ngoại lệ trong tín hiệu "quá tải toán tử là xấu".

Chỉ là bạn, với tư cách là một lập trình viên C (vì lý do lịch sử) hoặc Java (vì lý do cá nhân , xem bên dưới), bạn không thể cung cấp cho riêng mình.

Trong C ++, quá tải toán tử không phải là tùy chọn ...

Trong C ++, quá tải toán tử cho các kiểu dựng sẵn là không thể (và đây là một điều tốt), nhưng các kiểu do người dùng định nghĩa có thể có quá tải toán tử do người dùng định nghĩa .

Như đã nói trước đó, trong C ++ và ngược lại với Java, các kiểu người dùng không được coi là công dân hạng hai của ngôn ngữ, khi so sánh với các loại tích hợp. Vì vậy, nếu các loại tích hợp có toán tử, các loại người dùng cũng có thể có chúng.

Sự thật là, như toString(), clone(), equals()phương pháp này cho Java ( tức là bán tiêu chuẩn như ), C ++ nhà khai thác quá tải là rất nhiều một phần của C ++ mà nó trở nên tự nhiên như các nhà khai thác C ban đầu, hoặc trước khi phương pháp Java nêu.

Kết hợp với lập trình mẫu, quá tải toán tử trở thành một mẫu thiết kế nổi tiếng. Trong thực tế, bạn không thể đi rất xa trong STL mà không sử dụng các toán tử quá tải và các toán tử quá tải cho lớp của riêng bạn.

... nhưng nó không nên bị lạm dụng

Quá tải toán tử nên cố gắng tôn trọng ngữ nghĩa của toán tử. Không được trừ trong một +toán tử (như trong "không trừ trong một addhàm" hoặc "trả về crap trong một clonephương thức").

Quá tải diễn viên có thể rất nguy hiểm vì chúng có thể dẫn đến sự mơ hồ. Vì vậy, họ thực sự nên được dành riêng cho các trường hợp được xác định rõ. Đối với &&||, đừng bao giờ làm quá tải chúng trừ khi bạn thực sự biết những gì bạn đang làm, vì bạn sẽ mất đánh giá ngắn mạch mà các nhà khai thác bản địa &&||thích thú.

Vậy ... Ok ... Vậy tại sao Java không thể?

Bởi vì James Gosling đã nói như vậy:

Tôi đã bỏ quá tải toán tử như một lựa chọn khá cá nhân vì tôi đã thấy quá nhiều người lạm dụng nó trong C ++.

James Gosling. Nguồn: http://www.gotw.ca/publications/c_family_interview.htmlm

Vui lòng so sánh văn bản của Gosling ở trên với Stroustrup dưới đây:

Nhiều quyết định thiết kế C ++ bắt nguồn từ việc tôi không thích ép buộc mọi người thực hiện mọi thứ theo một cách đặc biệt nào đó [...] Thông thường, tôi bị cám dỗ ngoài vòng pháp luật một tính năng mà cá nhân tôi không thích, tôi đã từ chối vì tôi không nghĩ rằng mình đã làm vậy quyền buộc quan điểm của tôi về người khác .

Bjarne Stroustrup. Nguồn: Thiết kế và tiến hóa của C ++ (Nền tảng chung 1.3)

Toán tử quá tải sẽ có lợi cho Java?

Một số đối tượng sẽ được hưởng lợi rất nhiều từ quá tải toán tử (các loại cụ thể hoặc số, như BigDecimal, số phức, ma trận, container, iterator, bộ so sánh, trình phân tích cú pháp, v.v.).

Trong C ++, bạn có thể kiếm lợi từ lợi ích này nhờ sự khiêm tốn của Stroustrup. Trong Java, bạn chỉ đơn giản là say sưa vì lựa chọn cá nhân của Gosling .

Nó có thể được thêm vào Java không?

Các lý do cho việc không thêm quá tải toán tử hiện tại trong Java có thể là sự pha trộn của chính trị nội bộ, dị ứng với tính năng, mất lòng tin của các nhà phát triển (bạn biết, những kẻ phá hoại dường như ám ảnh các nhóm Java ...), khả năng tương thích với các JVM trước đó, thời gian để viết một đặc điểm kỹ thuật chính xác, vv ..

Vì vậy, đừng nín thở chờ đợi tính năng này ...

Nhưng họ làm điều đó trong C # !!!

Ừ ...

Mặc dù đây không phải là sự khác biệt duy nhất giữa hai ngôn ngữ, nhưng ngôn ngữ này không bao giờ làm tôi buồn cười.

Rõ ràng, những người C #, với "mọi nguyên thủy là một struct, và structxuất phát từ Object" , đã thử ngay từ lần thử đầu tiên.

Và họ làm điều đó bằng các ngôn ngữ khác !!!

Bất chấp tất cả FUD chống lại quá tải toán tử được xác định đã sử dụng, các ngôn ngữ sau hỗ trợ nó: 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 ...

Rất nhiều ngôn ngữ, với rất nhiều triết lý khác nhau (và đôi khi đối lập), và tất cả chúng đều đồng ý về điểm đó.

Thức ăn cho suy nghĩ ...


50
Đây là một câu trả lời tuyệt vời. Tôi không đồng ý với nó, nhưng nó vẫn là một câu trả lời tuyệt vời. Tôi nghĩ rằng các vấn đề có thể xảy ra với quá tải xấu vượt quá giá trị của quá tải tốt.
Douglas Leeder

69
@Doumund Leeder: Cảm ơn! Quá tải toán tử giống như OOP. Lần đầu tiên bạn học cách làm điều đó, bạn viết quá tải ở mọi nơi vì bạn sẽ đặt các lớp cơ sở và kế thừa ở mọi nơi (như, trớ trêu ngọt ngào, API Java). Nhưng điều này trôi qua khá nhanh và sau đó bạn đánh giá cao khả năng trong khi không lạm dụng nó. Kinh nghiệm 10 năm của tôi về C ++ là số lần quá tải xấu mà tôi thấy cả trong mã và mã từ các lập trình viên khác rất thấp, tôi tin rằng tôi có thể đếm chúng bằng một tay. Và điều này ít hơn rất nhiều so với số lượng lỗi tổng thể với sprintf, strcat, memset và tràn bộ đệm.
paercebal

11
@Doumund Leeder: Tôi tin rằng, sau khi thảo luận về điều đó trong một câu hỏi SO khác, khoảng cách giữa "người yêu" và "người ghét" quá tải của nhà điều hành có thể là do sự khác biệt trong cách tiếp cận mã của họ: "Kẻ ghét" có nhiều "chức năng" hơn là những gì quan trọng ", có nghĩa là họ mong đợi một chức năng để làm một việc, và một điều duy nhất. Do đó, các nhà khai thác nên làm việc như được thiết kế bởi ngôn ngữ. "Những người yêu thích" nói nhiều hơn về "các đối tượng nên cư xử", nghĩa là họ chấp nhận chức năng đó dễ dàng hơn (và do đó, các nhà khai thác) có thể thay đổi hành vi của họ theo loại tham số của họ.
paercebal

103
Câu trả lời sử thi. Một trong những bản sửa lỗi chất lượng nhất mà tôi từng đọc.
Sebastian Mach

7
@MaartenBodewes: Tất cả các ví dụ tôi đã viết ở trên và tất cả những điều làm phiền bạn là "với tư cách là một nhà phát triển, bạn có bị lừa bởi vì lựa chọn cá nhân của Gosling" không? Xin vui lòng, viết câu trả lời của riêng bạn, bảo vệ góc nhìn "bạn là nhà phát triển ngu ngốc, hãy để những người thiên tài quyết định cho bạn những gì bạn cần" . Thảo luận này không phục vụ mục đích.
paercebal

44

James Gosling thích thiết kế Java như sau:

"Có nguyên tắc này về việc di chuyển, khi bạn chuyển từ căn hộ này sang căn hộ khác. Một thử nghiệm thú vị là đóng gói căn hộ của bạn và đặt mọi thứ vào hộp, sau đó chuyển sang căn hộ tiếp theo và không giải nén bất cứ thứ gì cho đến khi bạn cần. Đang làm bữa ăn đầu tiên của bạn, và bạn đang lấy thứ gì đó ra khỏi hộp. Sau một tháng, bạn đã sử dụng nó để tìm ra những thứ trong cuộc sống mà bạn thực sự cần, và sau đó bạn lấy phần còn lại của công cụ - quên đi bạn thích nó hay nó tuyệt đến mức nào - và bạn chỉ cần vứt nó đi. Thật đáng kinh ngạc khi nó đơn giản hóa cuộc sống của bạn và bạn có thể sử dụng nguyên tắc đó trong tất cả các loại vấn đề thiết kế: không làm mọi thứ chỉ vì chúng Thật tuyệt hoặc chỉ vì chúng thú vị. "

Bạn có thể đọc bối cảnh của trích dẫn ở đây

Về cơ bản, quá tải toán tử là tuyệt vời cho một lớp mô hình một số loại điểm, tiền tệ hoặc số phức. Nhưng sau đó bạn bắt đầu chạy ra khỏi các ví dụ nhanh.

Một yếu tố khác là sự lạm dụng tính năng trong C ++ bởi các nhà phát triển làm quá tải các toán tử như '&&', '||', các toán tử cast và tất nhiên là 'mới'. Sự phức tạp do kết hợp điều này với việc vượt qua giá trị và ngoại lệ được đề cập rất rõ trong cuốn sách C ++ đặc biệt .


6
Bạn có thể cung cấp một ví dụ mã về "sự phức tạp của quá tải toán tử kết hợp với giá trị truyền và ngoại lệ" không? Mặc dù có một vài năm chơi với ngôn ngữ, và sở hữu và đã đọc tất cả các cuốn sách hiệu quả / đặc biệt về C ++, tôi không hiểu ý của bạn là gì.
paercebal

60
Những gì làm việc cho James Gosling sẽ không làm việc cho tất cả mọi người. Anh ta cực kỳ thiển cận vì đã ngoại suy thí nghiệm đóng gói "thú vị" của mình để có nghĩa là "Vứt bỏ mọi thứ trên thế giới mà tôi không cần, vì vậy không ai có thể sử dụng những thứ đó." Anh ấy rõ ràng không biết những gì tôi cần hoặc sử dụng.
BT

49
@BT: Hầu hết giác ngộ là quan điểm của Gosling khi so sánh với quan điểm của Stroustrup về vấn đề này : 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 ++, được quảng cáo rầm rộ so với cái khác, Java, được nhiều người thích" Đây là quảng cáo tiếp thị. Hãy nhớ rằng C ++ đã phát triển một mình, trong khi Java (và .NET) thu được lợi nhuận từ máy ủi tiếp thị. Không có gì lạ khi đối với một "ngôn ngữ được yêu thích rộng rãi", Java bị giới hạn trong các ứng dụng máy chủ, trong khi "bị thu hồi rộng rãi" (có lẽ bởi các nhà phát triển và quản lý Java muốn giảm chi phí sản xuất mã) C ++ đi từ mức rất cao máy chủ hiệu suất để trò chơi hiệu suất cao? [...]
paercebal

16
@Hassan: Mỗi ngôn ngữ đều có bản hack, các khái quát của Java là một ví dụ tuyệt vời về điều đó. Bây giờ, về 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: Lập trình viên xấu sẽ viết mã xấu bất kể ngôn ngữ. Chỉ cần cố gắng mô phỏng "tham chiếu qua" cho các tham số hàm trong Java để có ý tưởng. Tôi đã xem mã, và cười rất đau. Đây là loại thứ mà Gosling không sử dụng, do đó, cần có những bản hack khủng khiếp cần có trong Java, tuy nhiên, tồn tại tự nhiên, với chi phí bằng không, trong cả C # và C ++.
paercebal

22

Kiểm tra Boost.Units: liên kết văn bản

Nó cung cấp phân tích thứ nguyên không chi phí thông qua quá tải toán tử. Điều này có thể rõ ràng hơn bao nhiêu?

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

thực sự sẽ tạo ra "Năng lượng = 4 J" là chính xác.


1
"Làm thế nào chính xác nếu làm phức tạp bảo trì và nơi nào trên trái đất này mã này làm xáo trộn?"
Vịt mướp

13

Các nhà thiết kế Java đã quyết định rằng quá tải toán tử là rắc rối hơn giá trị của nó. Đơn giản như thế.

Trong một ngôn ngữ mà mọi biến đối tượng thực sự là một tham chiếu, quá tải toán tử sẽ có thêm rủi ro là khá phi logic - ít nhất là đối với một lập trình viên C ++. So sánh tình huống với quá tải toán tử = # của C # Object.EqualsObject.ReferenceEquals(hoặc bất cứ điều gì nó được gọi).


8

Groovy có quá tải toán tử và chạy trong JVM. Nếu bạn không quan tâm đến hiệu suất đạt được (sẽ nhỏ hơn mỗi ngày). Nó tự động dựa trên tên phương thức. ví dụ: '+' gọi phương thức 'cộng (đối số)'.


4
Tôi ước tất cả các ngôn ngữ nặng cú pháp với quá tải toán tử sẽ sử dụng kỹ thuật đó. Tôi chưa bao giờ hiểu tại sao họ phải phát minh ra một phiên bản đặc biệt của việc đặt tên và tra cứu phương pháp. Stroustrup không đề cập đến bất kỳ sự thay thế nào trong D & EC ++. Nhóm C # đã thực hiện đúng cách với cú pháp Linq ( where ...trở thành .Where(i => ... ). Nếu chỉ họ làm điều tương tự với các toán tử số học, thì nhiều thứ sẽ đơn giản và mạnh mẽ hơn. Java có lợi thế của một bảng xếp hạng sạch và có thể có được điều này (mặc dù vì những lý do đáng tin cậy, nó có lẽ sẽ không bao giờ).
Daniel Earwicker

@DanielEarwicker, tôi thường lưu ý rằng khi có những bất đồng phức tạp, mọi người sẽ gắn thẻ các động lực cho cả hai bên là "tôn giáo" trong tự nhiên.

@noah, tôi có thể sống với một tập hợp con giới hạn của quá tải toán tử như thế này, với điều kiện là có một thẻ đặc biệt cho các tên phương thức giữ cho chúng khác biệt về mặt trực quan. Một cái gì đó giống như định nghĩa một phương thức __plus () để thực hiện OL "+" và tránh xa những thứ quá tải như phôi và thậm chí là các mảng con. Điều tôi không sẵn lòng sống là cách C ++ và C # thấy phù hợp để thực hiện nó.

2
Không phải là một câu trả lời. Có nhiều ngôn ngữ chạy trên VM. Quá tải toán tử không nên là một lý do chính đáng để chuyển đổi ngôn ngữ.
Maarten Bodewes

6

Tôi nghĩ rằng đây có thể là một lựa chọn thiết kế có ý thức để buộc các nhà phát triển tạo ra các chức năng có tên rõ ràng truyền đạt ý định của họ. Trong C ++, các nhà phát triển sẽ làm quá tải các toán tử với chức năng thường không liên quan đến bản chất thường được chấp nhận của toán tử đã cho, khiến cho gần như không thể xác định được một đoạn mã nào mà không nhìn vào định nghĩa của toán tử.


14
In C++ developers would overload operators with functionality that would often have no relation to the commonly accepted nature of the given operator: Đây là một khẳng định vô cớ. Tôi là một nhà phát triển C ++ chuyên nghiệp từ 12 năm nay và tôi hiếm khi gặp phải vấn đề này. Trên thực tế, hầu hết các lỗi và lỗi thiết kế mà tôi thấy trong C ++ là ở mã kiểu C ( void *, phôi, v.v.)
paercebal

6
-1. Mỗi biến bạn gán là một ký hiệu, giống như các ký hiệu toán tử số học. Cho dù bạn sử dụng một cụm từ để đặt tên cho biến đó, một từ đơn hay một chữ cái, là quyết định của bạn (hoặc nhóm của bạn). Ai là người nói điều gì có ý nghĩa và điều gì không? Câu trả lời là bạn, lập trình viên. Trong toán học thuần túy, phép nhân giữa các ma trận có nghĩa là một số khác với phép nhân giữa hai số trong số học cơ bản. Tuy nhiên, chúng tôi sử dụng cùng một biểu tượng cho cả hai loại nhân.
Kỹ sư

2
@paercebal: Không may là khẳng định đúng. Bạn phải nhìn không xa hơn IOux để thấy nó hoạt động. Rất may, hầu hết các nhà phát triển có nhiều hạn chế hơn về việc phát minh ra ngữ nghĩa mới cho các nhà khai thác hiện có.
Ben Voigt

5
@BenVoigt: [...] Và tôi thậm chí không đề cập đến thực tế là addchức năng có thể thực sự bị sử dụng sai (như thực hiện phép nhân hoặc lấy một mutex) ... Việc lạm dụng được đề cập bởi user14128 không chỉ giới hạn ở các nhà khai thác, nhưng Có một nỗi sợ bệnh lý nào đó về việc quá tải toán tử mà tôi tin là xuất phát từ những ngày đầu của C so với C ++, một nỗi sợ không được sửa đổi ngay trong Java, nhưng may mắn thay, đã không đi vào C # ... Cuối cùng, tôn trọng ngữ nghĩa và viết các hàm / toán tử rõ ràng là công việc của nhà phát triển. Không phải ngôn ngữ.
paercebal

3
@ jbo5112: Ví dụ: cout << f() || g(); Các dấu ngoặc đơn không làm cho nó rõ ràng hơn, chúng làm cho nó chính xác. Và đó là các toán tử dịch chuyển bit không bị lạm dụng, chúng sẽ không cần thiết. Tại sao cout << (5&3) << endl;tốt hơn cout.fmt(5&3)(endl);? Sử dụng toán tử gọi hàm trên biến thành viên functor sẽ là một thiết kế tốt hơn cho các luồng so với việc sử dụng lại các toán tử bitwise chỉ vì glyph trông đẹp. Nhưng điều này là xa điều duy nhất sai với các luồng.
Ben Voigt

5

Vâng, bạn thực sự có thể tự bắn vào chân mình với quá tải toán tử. Nó giống như với con trỏ, mọi người phạm sai lầm ngu ngốc với họ và vì vậy nó đã quyết định lấy cái kéo đi.

Ít nhất tôi nghĩ đó là lý do. Dù sao tôi cũng đứng về phía bạn. :)



2
Đó là một cách suy nghĩ rất tệ. Bạn có thể tự bắn vào chân mình, chúng tôi thà chặt tay bạn xuống, vì vậy bạn sẽ không thể. Và tất nhiên chúng tôi cho rằng bạn là một thằng ngốc sẽ tự bắn mình.
ntj

5

Một số người nói rằng quá tải toán tử trong Java sẽ dẫn đến sự ám ảnh. Có bao giờ những người đó dừng lại để xem một số mã Java thực hiện một số phép toán cơ bản như tăng giá trị tài chính theo tỷ lệ phần trăm khi sử dụng BigDecimal? .... tính dài dòng của một bài tập như vậy trở thành minh chứng cho sự ám ảnh của chính nó. Trớ trêu thay, việc thêm quá tải toán tử vào Java sẽ cho phép chúng ta tạo lớp Tiền tệ của riêng mình, điều này sẽ làm cho mã toán học đó trở nên thanh lịch và đơn giản (ít bị ám ảnh hơn).


4

Nói rằng quá tải toán tử dẫn đến lỗi logic loại mà toán tử không khớp với logic hoạt động, nó giống như không nói gì. Loại lỗi tương tự sẽ xảy ra nếu tên hàm không phù hợp với logic hoạt động - vậy giải pháp là gì: giảm khả năng sử dụng chức năng!? Đây là một câu trả lời hài hước - "Không phù hợp cho logic hoạt động", mọi tên tham số, mọi lớp, chức năng hoặc bất cứ điều gì có thể không phù hợp về mặt logic. Tôi nghĩ rằng tùy chọn này nên có sẵn bằng ngôn ngữ lập trình đáng kính và những người cho rằng nó không an toàn - không có gì cả hai nói rằng bạn phải sử dụng nó. Hãy lấy C #. Họ rủ rê con trỏ nhưng hey - có tuyên bố 'mã không an toàn' - lập trình theo ý bạn có thể tự chịu rủi ro.


4

Về mặt kỹ thuật, có quá tải toán tử trong mọi ngôn ngữ lập trình có thể xử lý các loại số khác nhau, ví dụ như số nguyên và số thực. Giải thích: Thuật ngữ quá tải có nghĩa là chỉ đơn giản là có một số triển khai cho một chức năng. Trong hầu hết các ngôn ngữ lập trình, các triển khai khác nhau được cung cấp cho toán tử +, một cho số nguyên, một cho số thực, đây được gọi là nạp chồng toán tử.

Bây giờ, nhiều người thấy lạ khi Java có quá tải toán tử cho toán tử + để thêm các chuỗi lại với nhau và theo quan điểm toán học thì điều này thực sự lạ, nhưng nhìn từ quan điểm của nhà phát triển ngôn ngữ lập trình, không có gì sai khi thêm quá trình toán tử dựng sẵn cho toán tử + cho các lớp khác, ví dụ String. Tuy nhiên, hầu hết mọi người đều đồng ý rằng một khi bạn thêm nạp chồng cho + cho Chuỗi, thì nói chung cũng là một ý tưởng tốt để cung cấp chức năng này cho nhà phát triển.

Một sự hoàn toàn không đồng ý với lời ngụy biện rằng toán tử quá tải mã obfuscates, vì điều này được để cho nhà phát triển quyết định. Điều này là ngây thơ để suy nghĩ, và thành thật mà nói, nó đang già đi.

+1 để thêm quá tải toán tử trong Java 8.


Việc sử dụng Java +để ghép nối bất cứ thứ gì chuỗi-ish là IMHO khá gớm ghiếc, cũng như sự quá tải /trong C và FORTRAN cho phân chia toàn bộ và phân đoạn. Trong nhiều phiên bản của Pascal, việc sử dụng các toán tử số học trên bất kỳ loại số nào sẽ mang lại kết quả tương đương về mặt số cho việc chuyển các toán hạng sang Real, mặc dù các kết quả có thể không phải là toàn bộ số phải được đưa qua Trunchoặc Roundtrước khi chúng có thể được gán cho số nguyên.
supercat

2

Giả sử Java là ngôn ngữ triển khai thì a, b và c sẽ là các tham chiếu đến kiểu Complex với các giá trị ban đầu là null. Cũng giả sử rằng Complex là bất biến như BigInteger đã đề cập và BigDecimal bất biến tương tự , tôi nghĩ bạn có nghĩa như sau, khi bạn gán tham chiếu cho Complex được trả về từ việc thêm b và c, và không so sánh tham chiếu này với a.

Không phải là:

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

nhiều đơn giản hơn:

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

2
Tôi là ai ;) Bằng nhau có thể có nghĩa là gán hoặc so sánh, nhưng = luôn luôn là gán và == luôn là so sánh. Tên có thể tự giới thiệu các nguồn lớn lỗi.

1

Đôi khi thật tuyệt khi có quá tải toán tử, các lớp bạn bè và nhiều kế thừa.

Tuy nhiên tôi vẫn nghĩ rằng đó là một quyết định tốt. Nếu Java có quá tải toán tử thì chúng ta không bao giờ có thể chắc chắn về ý nghĩa của toán tử mà không xem qua mã nguồn. Hiện tại điều đó là không cần thiết. Và tôi nghĩ rằng ví dụ của bạn về việc sử dụng các phương thức thay vì quá tải toán tử cũng khá dễ đọc. Nếu bạn muốn làm cho mọi thứ rõ ràng hơn, bạn luôn có thể thêm một nhận xét trên các tuyên bố lông.

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

12
Tất nhiên, như đã đề cập ở nơi khác, bạn cũng không bao giờ có thể chắc chắn về ý nghĩa của hàm add.
Nhật thực

Đúng, tôi vẫn thấy thoải mái khi biết rằng ít nhất các nhà khai thác của tôi được mã hóa cứng. Tất nhiên, có các tính năng và sử dụng chúng hợp lý sẽ chỉ làm chúng ta tốt. Vấn đề là thật khó để biết ai đó đã sử dụng chúng hợp lý. Và bạn đồng ý về định nghĩa hợp lý. :-)

1
Nhận xét được thêm vào để làm rõ mã, là mã sẽ trông như thế nào trong ngôn ngữ hỗ trợ quá tải toán tử. Hơn nữa, thực tế bình luận được viết dưới dạng các nhà khai thác tin rằng sự phản đối của bạn đối với quá tải nhà điều hành.
Aluan Haddad

0

Đây không phải là một lý do tốt để không cho phép nó mà là một lý do thực tế:

Mọi người không phải lúc nào cũng sử dụng nó có trách nhiệm. Nhìn vào ví dụ này từ thư viện thư viện Python:

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

Đây là lời giải thích:

Toán tử / đã được sử dụng như một toán tử thành phần giữa hai lớp. Khi làm như vậy, lớp dưới có thể có một hoặc nhiều trường mặc định của nó bị quá tải theo lớp trên. (Bạn vẫn có thể đưa ra giá trị bạn muốn). Một chuỗi có thể được sử dụng như một lớp thô.


0

Các lựa chọn thay thế cho hỗ trợ gốc của quá tải toán tử Java

Vì Java không có quá tải toán tử, nên đây là một số lựa chọn thay thế bạn có thể xem xét:

  1. Sử dụng ngôn ngữ khác. Cả GroovyScala đều có quá tải toán tử và dựa trên Java.
  2. Sử dụng java-oo , một plugin cho phép quá tải toán tử trong Java. Lưu ý rằng nó KHÔNG độc lập với nền tảng. Ngoài ra, nó có nhiều vấn đề và không tương thích với các bản phát hành mới nhất của Java (tức là Java 10). ( Nguồn gốc StackOverflow )
  3. Sử dụng JNI , Giao diện gốc Java hoặc các lựa chọn thay thế. Điều này cho phép bạn viết các phương thức C hoặc C ++ (có thể là các phương thức khác?) Để sử dụng trong Java. Tất nhiên điều này cũng KHÔNG độc lập với nền tảng.

Nếu bất cứ ai biết về người khác, xin vui lòng bình luận, và tôi sẽ thêm nó vào danh sách này.


0

Mặc dù ngôn ngữ Java không hỗ trợ trực tiếp quá tải toán tử, bạn có thể sử dụng plugin trình biên dịch Manifold trong bất kỳ dự án Java nào để kích hoạt nó. Nó hỗ trợ Java 8 - 13 (phiên bản Java hiện tại) và được hỗ trợ đầy đủ trong IntelliJ IDEA.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.