Tại sao Chuỗi Java không có các phương thức thao tác chuỗi tĩnh?


17

Tại sao các nhà thiết kế Java không tạo các phiên bản tĩnh của các phương thức thao tác chuỗi trong java.lang.Stringlớp? Các phương thức sau đây là những gì tôi đề cập đến, nhưng câu hỏi có thể được mở rộng sang các phương thức không tĩnh khác trong lớp.

concat(String)                        substring(int, int)
replace(char, char)                   toLowerCase()
replace(CharSequence, CharSequence)   toLowerCase(Locale)
replaceAll(String, String)            toString()
replaceFirst(String, String)          toUpperCase()
split(String)                         toUpperCase(Locale)
split(String, int)                    trim()
substring(int)

Chỉ có các phiên bản không tĩnh của các phương thức này buộc phải kiểm tra null rõ ràng ở bất cứ nơi nào phương thức như vậy phải được gọi. Ví dụ, chỉ cần gọi example = example.trim()sẽ dẫn đến NullPulumException nếu String example = null. Vì vậy, lập trình viên phải thực hiện kiểm tra null bảng kê sau:

if (example != null)
    example = example.trim();
// OR:
example = (example==null) ? null : example.trim();
example = (example==null) ? null : example.substring(5);

Tôi sẽ tưởng tượng nó sẽ thuận tiện hơn rất nhiều nếu Stringcó các phiên bản tĩnh của các phương thức này (có lẽ là độc quyền ), sẽ lấy chuỗi đầu vào làm đối số đầu tiên:

example = String.trim(example);
example = String.replace(example, 'a', 'b');
example = String.substring(example, 5);

Điều này sẽ dẫn đến mã sạch hơn được viết bởi các lập trình viên, họ sẽ tự động xử lý các trường hợp null bằng cách trả về null, thay vì buộc các lập trình viên xử lý rõ ràng các trường hợp null. Việc trả về null có ý nghĩa với tôi vì việc thao tác một chuỗi null sẽ dẫn đến một chuỗi null , không phải là một lỗi.

Tại sao các nhà thiết kế Java không nghĩ về điều này khi họ thiết kế Stringlớp trong Java 1 hoặc Java 2 hoặc thậm chí thêm chức năng như vậy trong phiên bản Java sau này?


17
Tôi sẽ đề nghị thay thế "Tại sao các nhà thiết kế Java không nghĩ đến X" bằng "Tại sao các nhà thiết kế Java quyết định chống lại X". Cung cấp cho họ tín dụng cơ bản của việc không mù quáng với tùy chọn.
Avner Shahar-Kashtan

5
nulllà một trạng thái đặc biệt và nên được xử lý rõ ràng.
CodeInChaos

2
@KonradMorawski: Cá nhân tôi thấy rằng việc lạm dụng các phương pháp mở rộng. Nếu bạn có một giá trị null thì bạn đang cố gắng gọi các phương thức trên đó, bạn sẽ nhầm lẫn giữa những người đọc mã đó. Đó là một sự tái hiện kém của một Maybe<T>loại, tôi đoán vậy?
Phoshi

1
@Phoshi người ta phải nhớ rằng các phương thức mở rộng KHÔNG phải là phương thức thực, chúng chỉ là cú pháp đường. Nhưng tôi đồng ý rằng nó gây tranh cãi. Tôi muốn C # / Java cung cấp một số loại cú pháp không an toàn cú pháp tích hợp, như - nói - Kotlin. string name = employee?.personalData?.name. Chỉ là một phím tắt cho tất cả các if (employee != null)s lặp đi lặp lại . Câu hỏi SO liên quan: stackoverflow.com/questions/1196031/
Kiếm

2
@ADTC Erm, bạn có nhận ra rằng một chương trình không thể dự đoán trước rằng giá trị là null, phải không? - Bạn nên xác định hợp đồng giữa các giao diện của mình theo cách mà bạn thể chắc chắn nếu một giá trị được phép là null hay không. Không kiểm tra ở mọi nơi có nghĩa là bạn có vấn đề về thiết kế chương trình.
Uooo

Câu trả lời:


30

Việc trả về null có ý nghĩa với tôi vì việc thao tác một chuỗi null sẽ dẫn đến một chuỗi null, không phải là một lỗi

Vâng, đó là ý kiến của bạn . Những người khác có thể lập luận rằng các hoạt động của Chuỗi trên một đối tượng null, không chứa Chuỗi, không có ý nghĩa và do đó sẽ ném Ngoại lệ

Tại sao "các nhà thiết kế Java" đã làm hoặc không làm điều gì đó khó khăn với anwer.

Hiện đã có các thư viện có thể thực hiện các hoạt động Chuỗi an toàn null, ví dụ StringUtils của Apache Commons. Bạn có thể sử dụng thư viện đó hoặc tương tự nếu bạn cần chúng.


Bổ sung dựa trên ý kiến ​​của bạn

Đọc qua các bình luận, có vẻ như vấn đề thực sự bạn gặp phải là bạn phải kiểm tra nulltất cả các mã của mình. Đó có thể là một chỉ báo về một thiết kế chương trình tồi mà không có hợp đồng được xác định rõ ràng giữa các giao diện. Bạn có thể muốn đọc Tránh các câu lệnh!


1
Cảm ơn phản hồi của bạn. Tôi đoán câu hỏi thực sự là, tại sao chúng ta phải phụ thuộc vào một thư viện bên ngoài hoặc các lớp học tại nhà khi một tính năng cơ bản như vậy có thể trở thành một phần của Java tiêu chuẩn?
ADTC

4
@ADTC hãy đặt câu hỏi theo cách khác: Tại sao "các nhà thiết kế Java" nên đưa thứ gì đó vào ngôn ngữ, nếu đã có rất nhiều thư viện tốt ngoài đó, được kiểm tra và ghi chép tốt? Cách xác định "tính năng cơ bản" là dựa trên quan điểm. Không phải hầu hết mọi dự án đều cần một số thư viện bên ngoài cho "Chức năng cơ bản" (thử nghiệm đơn vị, chế giễu, ngày và giờ, ...)? Nó không phải là một vấn đề lớn, phải không?
Uooo

Một cách để đối phó với null là sử dụng Guava's Optional- code.google.com/p/guava-lologists/wiki/ mẹo
Konrad Morawski

10

IMO toàn bộ cách tiếp cận "if arg is null return null" đối với các phương thức viết một cách không cần thiết làm tăng độ phức tạp theo chu kỳ của chương trình của bạn.

Các lib Java ban đầu đã sử dụng cách tiếp cận null trả về, nhưng các anh chàng JDK luôn bảo vệ chống lại null với các ngoại lệ.

Với thời gian, tôi nghĩ rằng cách tiếp cận sau đã được đưa ra như là người chiến thắng rõ ràng.

Tại sao? Vì các trường null phá vỡ rất nhiều nguyên tắc oo. Bạn chỉ đơn giản là không thể coi họ là thành viên của một lớp đa hình. Điều này có nghĩa là bạn luôn phải mã hóa các trường hợp đặc biệt thành null và do đó độ phức tạp theo chu kỳ của bạn tăng vọt khi bạn phải cho rằng mọi thứ có thể là null.

Để giải quyết vấn đề của bạn, hãy xem xét sử dụng mẫu đối tượng null thay vì dựa vào các trường null.


2
Nhưng Stringkhông phải là đa hình, phải không? Và làm thế nào để bạn sử dụng mẫu đối tượng null cho một loại đã có sẵn như String?
Svick

Chuỗi không đa hình, không. Nhưng ngay cả như vậy bạn cũng muốn biết rằng bạn có thể gọi các phương thức cá thể trên bất kỳ tham chiếu chuỗi nào mà bạn có. Điều này làm việc cho tất cả các tài liệu tham khảo không null. Chuỗi đã có một đối tượng null: chuỗi rỗng. Giống như danh sách có danh sách trống. Vì vậy, hãy tự hỏi tại sao chuỗi trống không đủ trong trường hợp của bạn. Tôi đoán rằng bạn sử dụng một chuỗi null để gắn cờ một số trường hợp đặc biệt. Tự hỏi nếu bạn có thể sử dụng một lá cờ riêng cho trường hợp đặc biệt đó.
Alexander Torstling

@AlexanderTorstling: Một trường loại intmặc định là 0 chứ không phải null. Về mặt lý thuyết chúng ta có thể có một stringloại có giá trị mặc định sẽ là một chuỗi rỗng chứ không phải là null[một loại như vậy ngữ nghĩa có thể để Stringnhững gì intInteger]. Việc một người không trống stringsẽ chứa nội bộ tham chiếu đến một đối tượng heap sẽ là một chi tiết triển khai; không có lý do gì nó không thể hành xử theo ngữ nghĩa như người nguyên thủy.
supercat

@supercat: Đồng ý. Tôi nghĩ sẽ tốt hơn nếu cấm các giá trị null cho các từ chối trừ khi được bật rõ ràng. Các trường không null và cuối cùng mặc định. Nhiều loại đã có đối tượng hành vi null
Alexander Torstling

@AlexanderTorstling: Có các vị trí lưu trữ của tất cả các loại mặc định là all-byte-zero và việc cung cấp các kiểu cấu trúc có thể được gán thông qua tất cả các byte-sao chép, đơn giản hóa rất nhiều khung, nhưng khá nhiều hàm ý rằng các loại ngữ nghĩa tham chiếu có thể thay đổi phải mặc định vô giá trị. Tuy nhiên, sẽ rất hữu ích khi có hỗ trợ khung cho các loại giá trị sẽ gói gọn một tham chiếu duy nhất và sẽ "đóng hộp" như - và có thể bỏ hộp từ - loại tham chiếu đó .
supercat

3

Không nói đây là lý do, nhưng ngoại trừ toString, tất cả các phương thức này dễ dàng được gói gọn trong một lớp trình trợ giúp tĩnh, trong khi điều ngược lại là không đúng.

Tức là nếu bạn muốn Stings.toUpper (đầu vào chuỗi) mã dễ viết, nhưng nếu bạn muốn instance.toUpper và tất cả những gì bạn có là String.toUpper, thì điều đó là không thể ... trừ khi họ giới thiệu các phương thức mở rộng, điều mà nhiều khả năng thậm chí không được xem xét tại thời điểm đó.


Điểm tốt, mặc dù nhiều bình luận hơn là một câu trả lời. Nhưng ngay cả khi đã cho rằng, tại sao chúng ta phải phụ thuộc vào một thư viện bên ngoài hoặc các lớp sản xuất tại nhà khi một tính năng cơ bản như vậy có thể trở thành một phần của Java tiêu chuẩn?
ADTC

2
Đến bây giờ, bạn có String.format(tĩnh), nhưng bạn không thể làm được "something %s".format(id).
Konrad Morawski

@adtc: bởi vì đó không phải là một tính năng cơ bản của ngôn ngữ để thực hiện điều đó thông qua một lớp tĩnh. Bắt đầu từ tiền đề đó, có rất nhiều lý do để không làm điều đó - sự phình to mã / mã không cần thiết, chức năng trùng lặp, chi phí phát triển / bảo trì.
jmoreno

-2

Trong java, chuỗi đó là một đối tượng. Đó là một sự lựa chọn thiết kế.

Tham chiếu đối tượng có thể là null và là null nếu không có đối tượng được gán cho nó. Nếu bạn gọi một đối tượng như vậy, một NullPulumException sẽ bị ném. Đó là hành vi oo tiêu chuẩn.

Các nhà thiết kế Java đã lựa chọn có các chuỗi làm đối tượng và có các phương thức đó đưa ra các ngoại lệ con trỏ null là những gì bạn nhận được nếu bạn muốn các chuỗi là các đối tượng.

Tại sao các nhà thiết kế Java không nghĩ đến điều này khi họ thiết kế lớp String trong Java 1 hoặc Java 2 hoặc thậm chí thêm chức năng như vậy trong phiên bản Java sau này?

Có lẽ họ đã nghĩ về điều đó và chọn kết hợp hành vi oo.


Tôi có thể biết, làm thế nào là tạo các phương thức tĩnh để xử lý các trường hợp null không phải là hành vi OO không? Để rõ ràng, tôi không nói là như vậy, nhưng tôi đang cố gắng hiểu tại sao bạn nói quyết định thiết kế là kết hợp hành vi OO.
ADTC

Các phương thức tĩnh giống như các hàm. Và để sử dụng, bạn muốn chúng thậm chí không phải là một phần của lớp chuỗi thay vì là một phần của lớp tiện ích. Cũng giống như các hàm toán học.
Pieter B

4
Chỉ vì các chuỗi là các đối tượng không có nghĩa là lớp String không thể có các phương thức tĩnh. Và thực sự nó đã có một số, ví dụ. String.format. (Nhân tiện, tương tự trong C #). Vì vậy, nếu đó là một sự lựa chọn thiết kế, nó không thể chỉ xuất phát từ thực tế là các chuỗi là các đối tượng và nó không được áp dụng nhất quán.
Konrad Morawski

@PieterB Tôi sẽ không phàn nàn nếu ít nhất các phương thức đó được cung cấp trong một Stringslớp tiện ích, giống như chúng ta có Arrayslớp tiện ích ( mặc dù tôi hiểu rằng nó được tạo ra do sự cần thiết tuyệt đối do các mảng là một sự giao thoa kỳ lạ giữa nguyên thủy và đối tượng: ) ) .. miễn là nó là một phần của Java tiêu chuẩn và không yêu cầu phụ thuộc.
ADTC
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.