Tại sao không == so sánh giá trị chuỗi toán tử làm cho nó thành Java?


50

Mọi lập trình viên Java có thẩm quyền đều biết rằng bạn cần sử dụng String.equals () để so sánh một chuỗi, thay vì == vì == kiểm tra sự bằng nhau tham chiếu.

Khi tôi đang xử lý các chuỗi, hầu hết thời gian tôi đang kiểm tra sự bình đẳng giá trị hơn là sự bình đẳng tham chiếu. Đối với tôi, dường như sẽ trực quan hơn nếu ngôn ngữ cho phép so sánh các giá trị chuỗi bằng cách chỉ sử dụng ==.

Để so sánh, toán tử C = 's == kiểm tra sự bằng nhau về giá trị cho chuỗi s. Và nếu bạn thực sự cần thiết để kiểm tra sự bằng nhau tham chiếu, bạn có thể sử dụng String.ReferenceEquals.

Một điểm quan trọng khác là Chuỗi là bất biến, do đó không có hại gì khi thực hiện bằng cách cho phép tính năng này.

Có bất kỳ lý do cụ thể tại sao điều này không được triển khai trong Java không?


12
Bạn có thể muốn xem Scala, nơi ==bình đẳng đối tượng và eqlà đẳng thức tham chiếu ( ofps.oreilly.com/title/9780596155957/ Lỗi ).
Giorgio

Cũng như một ghi chú, và điều này có thể không giúp ích gì cho bạn, nhưng theo tôi nhớ, bạn có thể so sánh chuỗi ký tự chuỗi với '=='
Kgrover

10
@Kgrover: bạn có thể, nhưng đó chỉ là một sản phẩm phụ tiện lợi của sự bình đẳng tham chiếu và cách Java tích cực tối ưu hóa chuỗi kết hợp chuỗi chữ thành các tham chiếu đến cùng một đối tượng. Nói cách khác, nó hoạt động, nhưng vì những lý do sai.
tdammers

1
@aviv ==toán tử chỉ ánh xạ tới Equalsnếu ==toán tử được thực hiện theo cách đó. Hành vi mặc định cho ==giống như ReferenceEquals(thực tế, ReferenceEqualsđược định nghĩa là phiên bản đối tượng của ==)
Patrick Huizinga

3
Đó là hậu quả của các quyết định thiết kế có ý nghĩa rất lớn trong nhiều tình huống khác. Nhưng vì bạn dường như biết điều đó và đang hỏi điều này, tôi cảm thấy buộc phải phản bác lại câu hỏi của bạn: Tại sao phải có trường hợp sử dụng để so sánh tham chiếu Chuỗi?
Jacob Raihle

Câu trả lời:


90

Tôi đoán đó chỉ là sự nhất quán, hay "nguyên tắc ít ngạc nhiên nhất". Chuỗi là một đối tượng, vì vậy sẽ rất ngạc nhiên nếu được đối xử khác với các đối tượng khác.

Vào thời điểm Java xuất hiện (~ 1995), chỉ có một thứ gì đó giống như Stringlà hoàn toàn xa xỉ đối với hầu hết các lập trình viên, những người đã quen với việc biểu diễn các chuỗi như các mảng kết thúc null. StringHành vi của bây giờ là những gì trước đây, và điều đó thật tốt; tinh tế thay đổi hành vi sau này có thể có tác dụng đáng ngạc nhiên, không mong muốn trong các chương trình làm việc.

Là một lưu ý phụ, bạn có thể sử dụng String.intern()để có được một đại diện chính tắc (đã được thực hiện) của chuỗi, sau đó có thể so sánh với ==. Thực tập cần một chút thời gian, nhưng sau đó, việc so sánh sẽ thực sự nhanh chóng.

Ngoài ra, không giống như một số câu trả lời gợi ý, nó không phải là về việc hỗ trợ quá tải toán tử . Các +nhà điều hành (nối) hoạt động trên Strings mặc dù Java không hỗ trợ khai thác quá tải; nó chỉ đơn giản được xử lý như một trường hợp đặc biệt trong trình biên dịch, giải quyết StringBuilder.append(). Tương tự, ==có thể đã được xử lý như một trường hợp đặc biệt.

Vậy thì tại sao lại kinh ngạc với trường hợp đặc biệt +mà không phải với ==? Bởi vì, +đơn giản là không biên dịch khi áp dụng cho các Stringđối tượng không phải là điều rõ ràng. Chênh lệch hành vi của ==sẽ được nhiều ít rõ ràng và do đó đáng kinh ngạc hơn khi nó chạm bạn.


8
Trường hợp đặc biệt thêm ngạc nhiên.
Blrfl

17
Dây là một xa xỉ trong năm 1995? Có thật không?? Nhìn vào lịch sử của ngôn ngữ máy tính. Số lượng ngôn ngữ có một số loại chuỗi tại thời điểm đó sẽ vượt xa số ngôn ngữ không có. Có bao nhiêu ngôn ngữ ngoài C và hậu duệ của nó đã sử dụng mảng kết thúc null?
WarrenT

14
@WarrenT: Chắc chắn, một số ngôn ngữ (nếu không phải hầu hết) có một số loại chuỗi, nhưng các chuỗi thu thập rác, có khả năng Unicode là một điều mới lạ vào năm 1995, tôi nghĩ vậy. Ví dụ, Python đã giới thiệu các chuỗi Unicode với phiên bản 2.0, năm 2000. Chọn tính bất biến cũng là một lựa chọn gây tranh cãi tại thời điểm đó.
Joonas Pulakka

3
@JoonasPulakka Sau đó, có lẽ bạn nên chỉnh sửa câu trả lời của mình để nói điều đó. Bởi vì như hiện tại, phần tổng thể sang trọng của câu trả lời của bạn là khá sai.
Svick

1
Thực tập có một chi phí: bạn nhận được một chuỗi sẽ không bao giờ được giải quyết. (Chà, trừ khi bạn sử dụng công cụ thực tập của riêng mình mà bạn có thể vứt đi.)
Donal Fellows

32

James Gosling , người tạo ra Java, đã giải thích nó theo cách này vào tháng 7 năm 2000 :

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 ++. Tôi đã dành rất nhiều thời gian trong năm đến sáu năm qua để khảo sát mọi người về tình trạng quá tải của nhà điều hành và điều đó thực sự hấp dẫn, bởi vì bạn khiến cộng đồng bị chia thành ba phần: Có lẽ khoảng 20 đến 30 phần trăm dân số nghĩ về việc quá tải nhà điều hành là sinh sản của quỷ; ai đó đã làm gì đó với tình trạng quá tải toán tử vừa thực sự đánh dấu họ, bởi vì họ đã sử dụng like + để chèn danh sách và nó làm cho cuộc sống thực sự, thực sự khó hiểu. Rất nhiều vấn đề đó xuất phát từ thực tế là chỉ có khoảng nửa tá toán tử mà bạn có thể quá tải một cách hợp lý, nhưng vẫn có hàng ngàn hoặc hàng triệu toán tử mà mọi người muốn xác định - vì vậy bạn phải chọn,


50
À, vâng, cái cũ "hãy 'làm cùn công cụ nhọn hoắt để cái cớ không làm tổn thương chính họ".
Blrfl

22
@Blrfl: Nếu một công cụ tạo ra nhiều vấn đề hơn nó giải quyết thì đó không phải là một công cụ tốt. Tất nhiên, việc quyết định xem đây có phải là trường hợp quá tải toán tử có thể biến thành một cuộc thảo luận rất dài hay không.
Giorgio

15
-1. Điều này không trả lời câu hỏi nào cả. Java không có nhà khai thác quá tải. Các ==nhà khai thác quá tải cho các đối tượng và nguyên thủy. Các +nhà khai thác quá tải cho byte, short, int, long, float, double, Stringvà có lẽ một vài người khác tôi quên. Nó đã có hoàn toàn có thể quá tải ==cho Stringlà tốt.
Jörg W Mittag

10
@Jorg - không, không. Quá tải toán tử là không thể xác định ở cấp độ người dùng. Thực sự có một số trường hợp đặc biệt trong trình biên dịch nhưng hầu như không đủ điều kiện
AZ01

9
@Blrfl: Tôi không bận tâm đến việc các oafs tự làm tổn thương mình. Đó là khi họ vô tình thò mắt ra khiến tôi bực mình.
Jonas

9

Tính nhất quán trong ngôn ngữ. Có một toán tử hoạt động khác nhau có thể gây ngạc nhiên cho các lập trình viên. Java không cho phép người dùng quá tải toán tử - do đó, đẳng thức tham chiếu là ý nghĩa hợp lý duy nhất cho ==giữa các đối tượng.

Trong Java:

  • Giữa các kiểu số, ==so sánh đẳng thức số
  • Giữa các kiểu boolean, ==so sánh đẳng thức boolean
  • Giữa các đối tượng, ==so sánh danh tính tham chiếu
    • Sử dụng .equals(Object o)để so sánh các giá trị

Đó là nó. Quy tắc đơn giản và đơn giản để xác định những gì bạn muốn. Tất cả điều này được đề cập trong phần 15,21 của JLS . Nó bao gồm ba tiểu mục dễ hiểu, thực hiện và lý do.

Khi bạn cho phép quá tải== , hành vi chính xác không phải là thứ mà bạn có thể tìm đến JLS và đặt ngón tay lên một mặt hàng cụ thể và nói "đó là cách nó hoạt động", mã có thể trở nên khó hiểu. Hành vi chính xác của ==có thể gây ngạc nhiên cho người dùng. Mỗi khi bạn nhìn thấy nó, bạn phải quay lại và kiểm tra xem nó thực sự có nghĩa là gì.

Vì Java không cho phép quá tải các toán tử, nên người ta cần một cách để kiểm tra tính bằng giá trị mà bạn có thể ghi đè định nghĩa cơ sở. Vì vậy, nó đã được ủy thác bởi các lựa chọn thiết kế. ==trong Java kiểm tra số cho các kiểu số, đẳng thức boolean cho các kiểu boolean và đẳng thức tham chiếu cho mọi thứ khác (có thể ghi đè .equals(Object o)để làm bất cứ điều gì chúng muốn cho đẳng thức giá trị).

Đây không phải là vấn đề "có trường hợp sử dụng cho một hậu quả cụ thể của quyết định thiết kế này không" mà là "đây là một quyết định thiết kế để tạo điều kiện thuận lợi cho những điều khác, đây là hậu quả của nó."

Chuỗi thực tập , là một ví dụ như vậy về điều này. Theo JLS 3.10.5 , tất cả các chuỗi ký tự được thực hiện. Các chuỗi khác được thực tập nếu một người gọi .intern()chúng. Đó "foo" == "foo"là sự thật là kết quả của các quyết định thiết kế được thực hiện để giảm thiểu dung lượng bộ nhớ được sử dụng bởi các chuỗi ký tự. Ngoài ra, thực tập String là một cái gì đó ở cấp độ JVM có một chút tiếp xúc với người dùng, nhưng trong phần lớn các trường hợp, không phải là điều khiến lập trình viên quan tâm (và các trường hợp sử dụng cho lập trình viên không một cái gì đó cao trong danh sách cho các nhà thiết kế khi xem xét tính năng này).

Mọi người sẽ chỉ ra điều đó ++=bị quá tải cho String. Tuy nhiên, đó không phải là ở đây và cũng không có. Trường hợp nếu ==có một giá trị bằng nhau có nghĩa là Chuỗi (và chỉ Chuỗi), thì người ta sẽ cần một phương thức khác (chỉ tồn tại trong Chuỗi) cho đẳng thức tham chiếu. Hơn nữa, điều này sẽ làm phức tạp các phương thức lấy Object và mong ==muốn hành xử theo một cách và .equals()hành xử khác yêu cầu người dùng xử lý đặc biệt tất cả các phương thức đó cho String.

Hợp đồng nhất quán cho ==các Đối tượng là nó chỉ là đẳng thức tham chiếu và .equals(Object o)tồn tại cho tất cả các đối tượng cần kiểm tra sự bằng nhau về giá trị. Biến chứng này phức tạp quá nhiều thứ.


Cảm ơn đã dành thời gian trả lời. Đây sẽ là một câu trả lời tuyệt vời cho các câu hỏi khác mà tôi liên kết đến. Thật không may, điều này không phù hợp cho câu hỏi này. Tôi sẽ cập nhật OP với các giải thích dựa trên các ý kiến. Tôi đang tìm kiếm nhiều hơn cho các trường hợp sử dụng mà người dùng ngôn ngữ muốn có các phủ định sai khi so sánh các chuỗi. Ngôn ngữ cung cấp tính năng này là tính nhất quán, bây giờ tôi muốn chúng tôi tiến thêm một bước. Có lẽ nghĩ về điều này từ nhà thiết kế ngôn ngữ mới, nó có cần thiết không? (thật không may, không có lang-design.SE)
Anonsage

3
@ Trả lời không phải là âm tính giả. Họ không cùng một đối tượng. Đó là tất cả những gì nó đang nói. Tôi cũng phải chỉ ra rằng trong Java 8, new String("foo") == new String("foo")có thể đúng (xem Chuỗi trùng lặp ).

1
Về thiết kế ngôn ngữ, CS.SE quảng cáo rằng nó có thể nằm trong chủ đề đó.

Ồ, cảm ơn bạn! Tôi sẽ đăng câu hỏi thiết kế lang trong tương lai của tôi ở đó. :) Và, vâng, thật không may, 'âm tính giả' không phải là cách chính xác nhất để mô tả câu hỏi của tôi và những gì tôi đang tìm kiếm .. Tôi cần viết nhiều từ hơn để mọi người không phải đoán tôi là gì cố gắng để nói.
Anonsage

2
"Tính nhất quán trong ngôn ngữ" cũng giúp ích cho thuốc generic
Brendan

2

Java không hỗ trợ quá tải toán tử, có nghĩa là ==chỉ áp dụng cho các kiểu nguyên thủy hoặc tham chiếu. Bất cứ điều gì khác đòi hỏi phải có một phương thức. Tại sao các nhà thiết kế đã làm điều này là một câu hỏi chỉ họ có thể trả lời. Nếu tôi phải đoán, có lẽ vì quá tải toán tử mang lại sự phức tạp mà họ không muốn thêm vào.

Tôi không phải là chuyên gia về C #, nhưng các nhà thiết kế ngôn ngữ đó dường như đã thiết lập nó sao cho mọi nguyên thủy là một structvà mọi thứ structlà một đối tượng. Bởi vì C # cho phép quá tải toán tử, sự sắp xếp đó giúp cho mọi lớp không dễ dàng, không chỉ String, làm cho chính nó hoạt động theo cách "mong đợi" với bất kỳ toán tử nào. C ++ cho phép điều tương tự.


1
"Java không hỗ trợ quá tải toán tử, có nghĩa là == chỉ áp dụng cho các kiểu hoặc tham chiếu nguyên thủy. Bất kỳ điều gì khác đều yêu cầu gọi phương thức.": Người ta có thể thêm rằng nếu ==có nghĩa là bình đẳng chuỗi, chúng ta sẽ cần một ký hiệu khác cho đẳng thức tham chiếu.
Giorgio

@Giorgio: Chính xác. Xem nhận xét của tôi về câu trả lời của Gilad Naaman.
Blrfl

Mặc dù điều đó có thể được giải quyết bằng một phương thức tĩnh so sánh các tham chiếu của hai đối tượng (hoặc một toán tử). Giống như trong C # chẳng hạn.
Gilad Naaman

@GiladNaaman: Đó sẽ là một trò chơi có tổng bằng 0 vì nó gây ra vấn đề ngược lại với những gì Java hiện có: sự bình đẳng sẽ thuộc về một nhà điều hành và bạn phải gọi một phương thức để so sánh các tham chiếu. Hơn nữa, bạn phải áp đặt yêu cầu rằng tất cả các lớp thực hiện một cái gì đó có thể bị ràng buộc ==. Đó là thêm hiệu quả quá tải toán tử, điều này sẽ có ý nghĩa to lớn đối với cách triển khai Java.
Blrfl

1
@Blrfl: Không hẳn. Sẽ luôn có một cách xác định để so sánh tham chiếu ( ClassName.ReferenceEquals(a,b)) và cả ==toán tử và Equalsphương thức mặc định cả hai đều trỏ đến ReferenceEquals.
Gilad Naaman

2

Điều này đã được thực hiện khác nhau trong các ngôn ngữ khác.

Trong Object Pascal (Delphi / Free Pascal) và C #, toán tử đẳng thức được xác định để so sánh các giá trị, không phải tham chiếu khi hoạt động trên chuỗi.

Riêng trong Pascal, chuỗi là một kiểu nguyên thủy (một trong những điều tôi thực sự yêu thích về Pascal, việc nhận được NullreferenceException chỉ vì một chuỗi chưa được khởi tạo đơn giản là khó chịu) và có ngữ nghĩa sao chép trên văn bản do đó làm cho (hầu hết thời gian) hoạt động chuỗi giá rẻ (nói cách khác, chỉ đáng chú ý khi bạn bắt đầu nối chuỗi nhiều megabyte).

Vì vậy, đó là một quyết định thiết kế ngôn ngữ cho Java. Khi họ thiết kế ngôn ngữ, họ đã làm theo cách C ++ (như Std :: String) để các chuỗi là các đối tượng, đó là IMHO một hack để bù cho C thiếu một loại chuỗi thực, thay vì tạo các chuỗi nguyên thủy (mà chúng là).

Vì vậy, vì một lý do tại sao, tôi chỉ có thể suy đoán họ đã làm điều đó để dễ dàng về phía họ và không mã hóa toán tử tạo một ngoại lệ trên trình biên dịch thành chuỗi.


Làm thế nào để trả lời câu hỏi này?
gnat

Xem câu cuối cùng (mà tôi đã tách trong một đoạn thích hợp trong một chỉnh sửa).
Fabricio Araujo

1
IMHO, đáng Stringlẽ phải là một kiểu nguyên thủy trong Java. Không giống như các loại khác, trình biên dịch cần biết về String; hơn nữa, các hoạt động trên nó sẽ đủ phổ biến rằng đối với nhiều loại ứng dụng, chúng có thể gây ra một nút cổ chai hiệu năng (có thể được giảm bớt nhờ hỗ trợ riêng). Một string[chữ thường] thông thường sẽ có một đối tượng được phân bổ trên heap để giữ nội dung của nó, nhưng không có tham chiếu "bình thường" nào cho đối tượng đó tồn tại ở bất cứ đâu; do đó, nó có thể là một người độc thân Char[]hoặc Byte[]hơn là phải được chỉ định Char[]thông qua một đối tượng khác.
supercat

1

Trong Java, không có toán tử nào quá tải bất cứ điều gì và đó là lý do tại sao các toán tử so sánh chỉ bị quá tải cho các kiểu nguyên thủy.

Lớp 'Chuỗi' không phải là nguyên thủy, do đó nó không bị quá tải cho '==' và sử dụng mặc định so sánh địa chỉ của đối tượng trong bộ nhớ của máy tính.

Tôi không chắc chắn, nhưng tôi nghĩ rằng trong Java 7 hoặc 8 oracle đã tạo một ngoại lệ trong trình biên dịch để nhận ra str1 == str2str1.equals(str2)


"Tôi không chắc, nhưng tôi nghĩ rằng trong Java 7 hoặc 8 orory đã tạo ra một ngoại lệ trong trình biên dịch để nhận ra str1 == str2 là str1.equals (str2)": Tôi sẽ không ngạc nhiên: Oracle dường như ít quan tâm hơn với chủ nghĩa tối giản hơn Sun.
Giorgio

2
Nếu đúng, đó là một vụ hack rất xấu vì điều đó có nghĩa là giờ đây một lớp ngôn ngữ đối xử khác với tất cả các lớp khác và phá vỡ mã so sánh các tham chiếu. : - @
Blrfl

1
@WillihamTotland: Hãy xem xét trường hợp ngược lại. Hiện nay, nếu tôi có thể tạo hai chuỗi, s1s2và cung cấp cho họ những nội dung tương tự, họ vượt qua sự bình đẳng ( s1.equals(s2)so sánh) nhưng không phải là cùng một tham chiếu ( ==) so sánh bởi vì họ là hai đối tượng khác nhau. Thay đổi ngữ nghĩa của ==sự bình đẳng có nghĩa là sẽ s1 == s2đánh giá truenơi nó được sử dụng để đánh giá false.
Blrfl

2
@Brlfl: Mặc dù điều đó là đúng, nhưng điều đó nghe có vẻ như là một điều cực kỳ tồi tệ khi dựa vào vị trí đầu tiên, vì các chuỗi là các đối tượng bất biến, có thể thực hiện được.
Williham Totland

2
@Giorgio: "Nhà tiên tri Java 7 hoặc 8 đã tạo một ngoại lệ trong trình biên dịch để nhận ra str1 == str2 là str1.equals (str2)" Không, Đặc tả ngôn ngữ Java cho biết: Toán tử đẳng thức tham chiếu : "Nếu toán hạng của toán tử đẳng thức là cả hai loại tham chiếu hoặc loại null, sau đó hoạt động là bình đẳng đối tượng. " Đó là tất cả mọi người. Tôi cũng không tìm thấy bất cứ điều gì mới về điều này trong bản phác thảo sớm Java 8 .
David Tonhofer

0

Java dường như đã được thiết kế để duy trì một quy tắc cơ bản rằng ==toán tử phải hợp pháp bất cứ khi nào một toán hạng có thể được chuyển đổi thành loại của toán tử khác và nên so sánh kết quả của chuyển đổi đó với toán hạng không được chuyển đổi.

Quy tắc này hầu như không phải là duy nhất đối với Java, nhưng nó có một số ảnh hưởng sâu rộng (và không may IMHO) đối với việc thiết kế các khía cạnh liên quan đến loại khác của ngôn ngữ. Sẽ rõ ràng hơn khi chỉ định các hành vi ==liên quan đến các kết hợp cụ thể của các loại toán hạng và cấm kết hợp các loại X và Y ở đâu x1==y1x2==y1sẽ không ngụ ý x1==x2, nhưng các ngôn ngữ hiếm khi làm điều đó [theo triết lý đó, double1 == long1sẽ phải chỉ ra liệu double1không phải là một đại diện chính xác của long1, hoặc người khác từ chối biên dịch;int1==Integer1nên bị cấm, nhưng nên có một phương tiện không ném thuận tiện và hiệu quả để kiểm tra xem một đối tượng có phải là số nguyên được đóng hộp với giá trị cụ thể hay không (so sánh với thứ không phải là số nguyên được đóng hộp đơn giản false).

Liên quan đến việc áp dụng ==toán tử cho các chuỗi, nếu Java đã cấm so sánh trực tiếp giữa các toán hạng loại StringObject, thì nó cũng có thể tránh được những bất ngờ trong hành vi của nó ==, nhưng không có hành vi nào có thể thực hiện để so sánh như vậy sẽ không gây ngạc nhiên. Có hai tham chiếu chuỗi được giữ theo kiểu Objectkhác với các tham chiếu được giữ theo kiểu Stringsẽ ít gây ngạc nhiên hơn nhiều so với các hành vi đó khác với so sánh giữa các loại so sánh hợp pháp. Nếu String1==Object1là hợp pháp, điều đó có nghĩa là cách duy nhất cho các hành vi String1==String2Object1==Object2đối sánh String1==Object1sẽ là chúng khớp với nhau.


Tôi phải thiếu một cái gì đó, nhưng IMHO ==trên các đối tượng chỉ cần gọi (null-safe) bằng và một cái gì khác (ví dụ, ===hoặc System.identityEqual) nên được sử dụng để so sánh danh tính. Trộn nguyên thủy và các đối tượng ban đầu sẽ bị cấm (không có hộp số tự động trước 1,5) và sau đó có thể tìm thấy một số quy tắc đơn giản (ví dụ: unbox an toàn null, sau đó bỏ qua, sau đó so sánh).
maaartinus

@maaartinus: Một thiết kế ngôn ngữ tốt nên sử dụng các toán tử đẳng thức riêng biệt cho giá trị và đẳng thức tham chiếu. Mặc dù tôi đồng ý rằng về mặt khái niệm có thể có một int==Integertoán tử trả về falsenếu Integergiá trị null và so sánh các giá trị, cách tiếp cận đó sẽ không giống với hành vi của ==tất cả các trường hợp khác, trong đó nó vô tình ép cả hai toán hạng vào cùng loại trước đó so sánh chúng Cá nhân tôi tự hỏi liệu có tự động hủy hộp được đặt trong một nỗ lực để cho phép int==Integercó một hành vi không vô nghĩa ...
supercat

... kể từ khi tự động đặt hộp intvà thực hiện so sánh tham chiếu sẽ là ngớ ngẩn [nhưng sẽ không luôn thất bại]. Mặt khác, tôi thấy không có lý do gì để cho phép chuyển đổi ngầm có thể thất bại với NPE.
supercat

Tôi nghĩ rằng ý tưởng của tôi là phù hợp. Chỉ cần lưu ý rằng trong thế giới tốt hơn, ==không có gì để làm identityEquals. +++ "toán tử đẳng thức riêng biệt cho giá trị và đẳng thức tham chiếu" - nhưng toán tử nào? Tôi muốn xem xét cả hai nguyên thủy ==equalsnhư làm giá trị so sánh theo nghĩa là equalsvẻ tại giá trị của tham chiếu. +++ Khi ==có ý nghĩa equals, thì int==IntegerNÊN thực hiện autoboxing và so sánh các tham chiếu bằng cách sử dụng null-safe bằng. +++ Tôi sợ, ý tưởng của tôi không thực sự là của tôi, mà chỉ là những gì Kotlin làm.
maaartinus

@maaartinus: Nếu ==không bao giờ kiểm tra đẳng thức tham chiếu, thì nó có thể thực hiện một cách hợp lý một thử nghiệm bình đẳng giá trị an toàn null. Thực tế là nó không bình đẳng tham khảo kiểm tra, tuy nhiên, hạn chế nghiêm trọng như thế nào nó có thể xử lý tài liệu tham khảo so sánh giá trị / hỗn hợp mà không mâu thuẫn. Cũng lưu ý rằng Java được cố định trên khái niệm rằng các toán tử thúc đẩy cả hai toán hạng thành cùng loại, thay vì tạo ra các hành vi đặc biệt dựa trên sự kết hợp của các loại liên quan. Ví dụ, 16777217==16777216.0ftrả về truevì nó thực hiện chuyển đổi tổn thất của toán hạng đầu tiên sang float, trong khi ...
supercat

0

Nói chung, có lý do rất chính đáng để muốn có thể kiểm tra nếu hai tham chiếu đối tượng trỏ đến cùng một đối tượng. Tôi đã có rất nhiều lần tôi đã viết

Address oldAddress;
Address newAddress;
... populate values ...
if (oldAddress==newAddress)
... etc ...

Tôi có thể có hoặc không có chức năng bằng trong những trường hợp như vậy. Nếu tôi làm, hàm bằng có thể so sánh toàn bộ nội dung của cả hai đối tượng. Thường thì nó chỉ so sánh một số định danh. "A và B là các tham chiếu đến cùng một đối tượng" và "A và B là hai đối tượng khác nhau có cùng nội dung", tất nhiên, là hai ý tưởng rất khác nhau.

Có lẽ đúng là đối với các đối tượng bất biến, như String, đây không phải là vấn đề. Với các đối tượng bất biến, chúng ta có xu hướng nghĩ về đối tượng và giá trị là cùng một thứ. Chà, khi tôi nói "chúng ta", ít nhất tôi có nghĩa là "tôi".

Integer three=new Integer(3);
Integer triangle=new Integer(3);
if (three==triangle) ...

Tất nhiên điều đó trả về sai, nhưng tôi có thể thấy ai đó nghĩ rằng nó phải đúng.

Nhưng một khi bạn nói rằng == so sánh các thẻ điều khiển tham chiếu và không phải nội dung cho các Đối tượng nói chung, việc tạo một trường hợp đặc biệt cho Chuỗi sẽ có khả năng gây nhầm lẫn. Như một người khác ở đây đã nói, nếu bạn muốn so sánh tay cầm của hai đối tượng String thì sao? Sẽ có một số chức năng đặc biệt để làm điều đó chỉ cho Chuỗi?

Và thế còn ...

Object x=new String("foo");
Object y=new String("foo");
if (x==y) ...

Điều đó có sai vì chúng là hai đối tượng khác nhau hay đúng vì chúng là Chuỗi có nội dung bằng nhau?

Vì vậy, có, tôi hiểu làm thế nào các lập trình viên bị nhầm lẫn bởi điều này. Tôi đã tự làm điều đó, ý tôi là viết nếu myString == "foo" khi tôi có nghĩa là nếu myString.equals ("foo"). Nhưng thiếu thiết kế lại ý nghĩa của toán tử == cho tất cả các đối tượng, tôi không thấy cách giải quyết nó.


Lưu ý rằng các ngôn ngữ hiện đại khác trên JVM, như Scala, sử dụng ==để có nghĩa là "các chuỗi bằng nhau".
Andres F.

@AresresF. (nhún vai) Trong Java, "<" có nghĩa là "nhỏ hơn", trong khi ở XML, nó "mở một thẻ". Trong VB "=" có thể có nghĩa là "bằng", trong khi trong Java, nó chỉ được sử dụng để gán. V.v. Hầu như không ngạc nhiên khi các ngôn ngữ khác nhau sử dụng các ký hiệu khác nhau để chỉ cùng một thứ hoặc cùng một biểu tượng để có nghĩa là những thứ khác nhau.
Jay

Đó không phải là một câu trả lời của bạn. Đó chỉ là một bình luận. Tôi chỉ chỉ ra rằng một ngôn ngữ hiện đại hơn Java đã có cơ hội thiết kế lại ý nghĩa của nó ==, giống như bạn đã đề cập ở cuối.
Andres F.

@AresresF. Và câu trả lời của tôi không phải là một lời khai tại bình luận của bạn, chỉ nói rằng các ngôn ngữ khác nhau tiếp cận những vấn đề này theo những cách khác nhau. :-) Tôi thực sự thích cách VB xử lý việc này ... tạm dừng những tiếng rít và tiếng la ó từ những người ghét VB ... "=" luôn so sánh giá trị (đối với các loại do hệ thống xác định), cho dù là nguyên thủy hay đối tượng. "Is" so sánh tay cầm của hai đối tượng. Điều đó có vẻ trực quan hơn với tôi.
Jay

Chắc chắn rồi. Nhưng Scala gần với Java hơn Visual Basic. Tôi muốn nghĩ rằng các nhà thiết kế của Scala nhận ra rằng việc sử dụng Java ==dễ bị lỗi.
Andres F.

0

Đây là một câu hỏi hợp lệ cho Strings, và không chỉ cho các chuỗi, mà còn cho các đối tượng bất biến khác đại diện cho một số "giá trị", ví dụ Double, BigIntegervà thậm chí InetAddress.

Để làm cho ==toán tử có thể sử dụng được với Chuỗi và các lớp giá trị khác, tôi thấy ba lựa chọn thay thế:

  • Trình biên dịch biết về tất cả các lớp giá trị này và cách so sánh nội dung của chúng. Nếu đó chỉ là một số ít các lớp trong java.langgói, tôi sẽ xem xét điều đó, nhưng điều đó không bao gồm các trường hợp như InetAddress.

  • Cho phép toán tử quá tải để một lớp xác định ==hành vi so sánh của nó .

  • Xóa các hàm tạo công khai và có các phương thức tĩnh trả về các thể hiện từ một nhóm, luôn trả về cùng một thể hiện cho cùng một giá trị. Để tránh rò rỉ bộ nhớ, bạn cần một cái gì đó như SoftReferences trong nhóm, vốn không tồn tại trong Java 1.0. Và bây giờ, để duy trì khả năng tương thích, các nhà String()xây dựng không thể được gỡ bỏ nữa.

Điều duy nhất vẫn có thể được thực hiện ngày hôm nay là giới thiệu quá tải toán tử và cá nhân tôi không muốn Java đi theo con đường đó.

Đối với tôi, khả năng đọc mã là quan trọng nhất và một lập trình viên Java biết rằng các toán tử có nghĩa cố định, được định nghĩa trong đặc tả ngôn ngữ, trong khi các phương thức được xác định bởi một số mã và ý nghĩa của chúng phải được tìm kiếm trong Javadoc của phương thức. Tôi muốn ở lại với sự khác biệt đó ngay cả khi điều đó có nghĩa là các phép so sánh Chuỗi sẽ không thể sử dụng ==toán tử.

Chỉ có một khía cạnh so sánh của Java gây khó chịu cho tôi: hiệu quả của tự động đấm bốc và -unboxing. Nó che giấu sự phân biệt giữa kiểu nguyên thủy và kiểu bao bọc. Nhưng khi bạn so sánh chúng với ==, chúng RẤT khác nhau.

    int i=123456;
    Integer j=123456;
    Integer k=123456;
    System.out.println(i==j);  // true or false? Do you know without reading the specs?
    System.out.println(j==k);  // true or false? Do you know without reading the specs?
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.