Dlaczego ciąg Java String nie ma metod manipulacji ciągiem statycznym?

17

Dlaczego projektanci Java nie stworzyli statycznych wersji metod manipulacji ciągami w java.lang.Stringklasie? Odnoszę się do następujących metod, ale pytanie można rozszerzyć również na inne metody niestatyczne w klasie.

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)

Posiadanie tylko niestatycznych wersji tych metod wymusza jawne sprawdzanie wartości NULL wszędzie tam, gdzie taka metoda musi zostać wywołana. Na przykład zwykłe wywołanie example = example.trim()prowadziłoby do NullPointerException if String example = null. Dlatego programista musi wykonać następujące sprawdzenie wartości zerowej płyty kotłowej:

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

Wyobrażam sobie, że znacznie wygodniej byłoby Stringmieć statyczne wersje tych metod (być może nawet wyłącznie ), które przyjmowałyby ciąg wejściowy jako pierwszy argument:

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

Doprowadziłoby to do czystszego kodu napisanego przez programistów, który automatycznie zajmowałby się zerowymi przypadkami, po prostu zwracając null, zamiast zmuszania programistów do jawnej obsługi przypadków zerowych. Powrocie null sens dla mnie, ponieważ manipulowanie zerowy ciąg powinno skutkować pustym ciągiem, a nie błędów.

Dlaczego projektanci Java o tym nie pomyśleli, projektując Stringklasę w Javie 1 lub Java 2, ani nawet nie dodając takiej funkcjonalności w późniejszej wersji Java?

ADTC
źródło
17
Sugerowałbym zastąpienie „Dlaczego projektanci Java nie myśleli o X” na „Dlaczego projektanci Java nie zdecydowali się na X”. Daj im podstawową zaletę, że nie są ślepi na tę opcję.
Avner Shahar-Kashtan,
5
nulljest stanem wyjątkowym i należy go traktować wyraźnie.
CodesInChaos,
2
@KonradMorawski: Osobiście uważam, że jest to rażące niewłaściwe użycie metod rozszerzenia. Jeśli masz wartość zerową, to co, u licha, próbujesz wywołać na niej metody, po prostu wprowadzisz w błąd wszystkich, którzy czytają ten kod. Maybe<T>Chyba słaba reimplementacja tego typu?
Phoshi,
1
@Phoshi jeden ma pamiętać, że rozszerzenie metod nie są prawdziwe metody, są one po prostu cukier składni. Ale zgadzam się, że jest to kontrowersyjne. Chciałbym, żeby C # / Java oferował jakieś wbudowane składniowe zerowe bezpieczeństwo, jak - powiedzmy - Kotlin. string name = employee?.personalData?.name. Tak jak skrót do wszystkich tych powtarzających się if (employee != null)s. Powiązane SO pytanie: stackoverflow.com/questions/1196031/...
Konrad Morawski
2
@ ADTC Erm, zdajesz sobie sprawę, że program nie może przewidzieć z góry, że wartość jest zerowa, prawda? - Należy określić umów pomiędzy swoimi interfejsami w taki sposób, że może być pewien, czy to jest wartość nie może być zerowa lub nie. Sprawdzanie zera wszędzie oznacza, że ​​masz problem z projektem programu.
Uooo,

Odpowiedzi:

30

Zwracanie wartości null ma dla mnie sens, ponieważ manipulowanie łańcuchem pustym powinno skutkować łańcuchem pustym, a nie błędem

To jest twoja opinia. Inni mogą argumentować, że operacje na łańcuchach znaków na pustym obiekcie, który nie zawiera łańcucha, nie mają sensu i dlatego powinny zgłaszać wyjątek

Dlaczego „projektanci Java” zrobili lub nie zrobili czegoś, trudno jest odpowiedzieć.

Istnieją już biblioteki, które mogą wykonywać zerowe operacje na łańcuchach, np. StringUtils z Apache Commons. Możesz użyć tej lub podobnych bibliotek, jeśli ich potrzebujesz.


Dodanie na podstawie twoich komentarzy

Czytając komentarze, wydaje się, że prawdziwym problemem jest to, że musisz sprawdzić nullcały kod. Może to wskazywać na zły projekt programu bez jasno określonych umów między interfejsami. Możesz przeczytać Unikanie instrukcji „! = Null” w Javie?

Uooo
źródło
1
Dziękuję za twój komentarz. Chyba prawdziwe pytanie brzmi: dlaczego musimy polegać na zewnętrznej bibliotece lub klasach domowych, skoro tak podstawową funkcję można włączyć do standardowej Javy?
ADTC,
4
@ ADTC postawmy pytanie na odwrót: dlaczego „projektanci Java” powinni zawierać coś w języku, skoro istnieje już wiele dobrych bibliotek, dobrze przetestowanych i udokumentowanych? Jak zdefiniować „podstawową cechę”, opiera się na opiniach. Czy przeważnie każdy projekt nie potrzebuje biblioteki zewnętrznej do „podstawowej funkcjonalności” (testowanie jednostkowe, drwiny, data i godzina, ...)? To nie jest wielka sprawa, prawda?
Uooo,
Jednym ze sposobów radzenia sobie z zerami jest użycie Guava's Optional- code.google.com/p/guava-libraries/wiki/…
Konrad Morawski
10

IMO całe podejście „jeśli argument jest zerowy zwraca null” niepotrzebnie zwiększa cykliczną złożoność twojego programu.

Wczesne biblioteki Java korzystały z metody return null, ale chłopaki JDK zawsze chronili się przed wartością null z wyjątkami.

Z czasem myślę, że to drugie podejście okazało się wyraźnym zwycięzcą.

Dlaczego? Ponieważ pola zerowe łamią wiele zasad oo. Po prostu nie można ich traktować jako członków klasy polimorficznej. Oznacza to, że zawsze musisz zakodować specjalne przypadki dla wartości zerowej, a zatem twoja cykliczność rośnie, gdy musisz założyć, że wszystko może być zerowe.

Aby rozwiązać problemy, rozważ użycie wzorca obiektu zerowego zamiast polegania na polach zerowych.

Alexander Torstling
źródło
2
Ale Stringczy nie jest polimorficzny, nie? W jaki sposób używasz wzorca obiektu zerowego dla już istniejącego typu, takiego jak String?
svick,
Łańcuch nie jest polimorficzny, nie. Ale mimo to chciałbyś wiedzieć, że możesz wywoływać metody instancji w dowolnym odwołaniu do łańcucha. Działa to dla wszystkich odwołań niepustych. Ciąg ma już pusty obiekt: pusty ciąg. Podobnie jak listy mają pustą listę. Więc zadaj sobie pytanie, dlaczego pusty ciąg nie jest wystarczający w twoim przypadku. Myślę, że używasz łańcucha zerowego do oznaczenia jakiegoś specjalnego przypadku. Zadaj sobie pytanie, czy możesz użyć osobnej flagi dla tego specjalnego przypadku.
Alexander Torstling,
@AlexanderTorstling: Domyślnie pole typu intma wartość zero null. Pod względem koncepcyjnym powinna istnieć możliwość posiadania stringtypu, którego domyślną wartością byłby pusty ciąg, a nie null[taki typ mógłby semantycznie odpowiadać temu, Stringco intjest Integer]. Fakt, że niepuste stringwewnętrznie zawierałoby odwołanie do obiektu sterty, byłby szczegółem implementacji; nie ma powodu, dla którego nie mógłby zachowywać się semantycznie jak prymityw.
supercat
@supercat: Zgoda. Myślę, że lepiej byłoby zabronić wartości zerowych dla referencji, o ile nie zostało to wyraźnie włączone. Domyślne pola niepuste i końcowe. Wiele typów ma już obiekty zachowania zerowego
Alexander Torstling,
@AlexanderTorstling: Posiadanie miejsc przechowywania wszystkich typów domyślnie na all-bytes-zero i zapewnienie, że typy struktur można przypisywać za pomocą all-bytes-copy, znacznie upraszcza strukturę, ale prawie implikuje, że typy ze zmienną semantyką odniesienia muszą domyślnie mieć wartość zero. Przydałoby się jednak wsparcie ramowe dla typów wartości, które zawierałyby pojedyncze odniesienie i „blokowałyby” jako - i mogłyby się rozpakować - z tego typu referencji .
supercat,
3

Nie mówię, że to jest powód, ale oprócz toString, wszystkie te metody można łatwo zamknąć w statycznej klasie pomocniczej, podczas gdy odwrotna sytuacja nie jest prawdą.

To znaczy, jeśli chcesz Stings.toUpper (ciąg wejściowy), kod jest łatwy do napisania, ale jeśli chcesz instance.toUpper i wszystko, co masz, to String.toUpper, cóż, to niemożliwe ... chyba że wprowadzą metody rozszerzenia, co najprawdopodobniej nawet wtedy nie było brane pod uwagę.

jmoreno
źródło
Dobra uwaga, choć bardziej komentarz niż odpowiedź. Ale nawet biorąc to pod uwagę, dlaczego musimy polegać na zewnętrznej bibliotece lub klasach domowych, skoro tak podstawową funkcję można włączyć do standardowej Javy?
ADTC 12.12.13 o
2
Na razie masz String.format(statyczny), ale nie możesz "something %s".format(id).
Konrad Morawski,
@adtc: ponieważ nie jest to podstawowa funkcja języka, aby to robić za pomocą klasy statycznej. Poczynając od tej przesłanki, istnieje wiele powodów, aby tego nie robić - niepotrzebny rozrost kodu / kodu, zduplikowana funkcjonalność, koszty rozwoju / utrzymania.
jmoreno
-2

W Javie ten ciąg znaków jest obiektem. To wybór projektu.

Odwołania do obiektów mogą mieć wartość NULL i są zerowe, jeśli do obiektu nie jest przypisany żaden obiekt. Jeśli wywołasz taki obiekt, zgłoszony zostanie wyjątek NullPointerException. To standardowe zachowanie oo.

Projektanci Java zdecydowali się na posiadanie ciągów jako obiektu i stosowanie tych metod do zgłaszania wyjątków o zerowym wskaźniku, co otrzymujesz, jeśli chcesz, aby ciągi były obiektami.

Dlaczego projektanci Java o tym nie pomyśleli, projektując klasę String w Javie 1 lub Java 2, a nawet nie dodając takiej funkcjonalności w późniejszej wersji Java?

Prawdopodobnie pomyśleli o tym i zdecydowali się uwzględnić zachowanie oo.

Pieter B.
źródło
Czy mogę wiedzieć, w jaki sposób tworzenie statycznych metod zajmujących się przypadkami zerowymi, a nie zachowanie OO ? Dla jasności, nie mówię, że tak, ale staram się zrozumieć, dlaczego według ciebie decyzja projektowa obejmuje zachowanie OO.
ADTC 12.12.2013
Metody statyczne bardziej przypominają funkcje. I do celów, które chcesz, nie powinny nawet należeć do klasy ciągów, a raczej do klasy użytkowej. Podobnie jak funkcje klasy matematycznej.
Pieter B
4
To, że ciągi są obiektami, nie oznacza, że ​​klasa String nie może mieć metod statycznych. I rzeczywiście ma już kilka, np. String.format. (Nawiasem mówiąc, to samo w C #). Więc jeśli jest to wybór projektowy, nie może wynikać wyłącznie z faktu, że łańcuchy są obiektami i nie są stosowane konsekwentnie.
Konrad Morawski,
@PieterB Nie narzekałbym, gdyby przynajmniej takie metody zostały zapewnione w Stringsklasie użyteczności, tak jak mamy Arraysklasę użyteczności ( chociaż rozumiem, że została stworzona z czystej konieczności, ponieważ tablice są dziwnym skrzyżowaniem prymitywów i obiektów: ) ) .. tak długo, jak był częścią standardowej Java i nie wymagał zależności.
ADTC