Kiedy używać klasy opakowania i typu pierwotnego

Odpowiedzi:

69

Inni wspominali, że niektóre konstrukcje, takie jak Collectionswymagają obiektów, i że obiekty mają więcej narzutów niż ich prymitywne odpowiedniki (pamięć i boks).

Inną kwestią jest:

Przydatne może być zainicjowanie obiektów nulllub wysłanie nullparametrów do metody / konstruktora w celu wskazania stanu lub funkcji. Nie można tego zrobić z prymitywami.

Wielu programistów inicjuje liczby na 0 (domyślnie) lub -1, aby to oznaczyć, ale w zależności od scenariusza może to być niepoprawne lub wprowadzające w błąd.

Spowoduje to również ustawienie sceny, NullPointerExceptiongdy coś jest używane nieprawidłowo, co jest znacznie bardziej przyjazne dla programisty niż jakiś arbitralny błąd w przyszłości .

pstanton
źródło
15
„Przydatne może być zainicjowanie obiektów do wartości null”. Nie może być przydatne, ponieważ jest to nieprawidłowe, jeśli pole jest opcjonalne, należy to wyraźnie zaznaczyć.
Eddie Jamsession
8
lol @EddieJamsession dzięki, nigdy więcej nie zainicjuję zresetowania. (pfffft)
pstanton
@EddieJamsession Jeśli zainicjowanie obiektów na wartość null jako sposób wskazania błędu podczas ustawiania rzeczywistej wartości nie powiodło się, w jaki inny sposób można zaproponować rozwiązanie tego problemu? Och, właśnie sobie uświadomiłem, kiedy to piszę: wyjątki. Wyjątek NullPointerException jest zbyt ogólny; lepiej byłoby użyć bardzo konkretnych wyjątków niestandardowych, aby wskazać, co poszło nie tak. Dobrze, będę to robić od teraz ...
klaar
1
@EddieJamsession Po przeczytaniu informacji na ten temat natknąłem się na koncepcję obiektu opcjonalnego. Dobrze.
klaar
18

Ogólnie rzecz biorąc, powinieneś używać typów pierwotnych, chyba że z jakiegoś powodu potrzebujesz obiektu (np. Aby umieścić w kolekcji). Nawet wtedy rozważ inne podejście, które nie wymaga obiektu, jeśli chcesz zmaksymalizować wydajność numeryczną. Zaleca się to w dokumentacji , a ten artykuł pokazuje, jak automatyczne boksowanie może powodować duże różnice w wydajności.

Matthew Flaschen
źródło
3
wydajność nie jest tak wielka, że ​​czytelność / niezawodność kodu powinna zająć drugie miejsce.
pstanton
2
Po pierwsze, właściwe użycie prymitywów nie spowoduje, że kod będzie nieczytelny. Po drugie, w niektórych przypadkach wpływ na wydajność jest znaczący. Absurdem jest powiedzieć, że nigdy nie jest.
Matthew Flaschen
1
@pstanton: proszę wyjaśnić, dlaczego Integerjest bardziej czytelny niż int.
Stephen C
W wielu przypadkach Integer nie jest bardziej czytelny niż int iw takich przypadkach zawsze będę używał int, lub jeśli wiem, że pewna zmienna NIGDY nie będzie zerowa, użyję int, ponieważ int jest, jak wskazałeś, nieco bardziej wydajne. Jednak w wielu przypadkach innemu programistowi łatwiej jest zrozumieć stan zmiennej, gdy obiekt jest używany, ponieważ można go zainicjować do wartości null, aby zaznaczyć, że nie jest gotowy. Na przykład, jeśli masz niezapisany rekord bazy danych, który ma unikatowy rosnący identyfikator liczbowy, czy ten identyfikator powinien wynosić 0, -1 lub null, zanim zostanie przypisany prawidłowy identyfikator? W takim przypadku obiekty są lepsze.
pstanton
jeśli chodzi o wydajność - w szczególnych przypadkach spadek wydajności może być znaczący, np. jeśli tworzysz wiele takich obiektów bardzo szybko lub jeśli wiele, wiele z nich narasta w pewnym okresie czasu. Spodziewałbym się jednak, że przyzwoity programista będzie w stanie zidentyfikować te specjalne przypadki, odpowiednio je unikaj lub poprawiaj. Nigdy nie powiedziałem, że to NIGDY nie ma znaczenia, jednak ogólnie rzecz biorąc, nie jest.
pstanton
12

Moim zdaniem, jeśli składowe mojej klasy są zmiennymi opakowującymi, nie opiera się na wartościach domyślnych, co jest zachowaniem przyjaznym dla programistów.

1.

class Person {
   int SSN ; // gets initialized to zero by default 
}

2.

class PersonBetter {
  Integer SSN; //gets initialized to null by default
}

W pierwszym przypadku nie można pozostawić niezainicjowanej wartości numeru PESEL. Może to zaszkodzić, jeśli nie sprawdzisz, czy wartość została ustawiona przed próbą jej użycia.

W drugim przypadku możesz zachować inicjalizację numeru SSN z wartością null. Co może prowadzić do wyjątku NullPointerException, ale jest lepsze niż nieświadome wstawianie wartości domyślnych (zero) jako numeru SSN do bazy danych za każdym razem, gdy próbujesz go użyć bez inicjowania pola SSN.

Cody
źródło
3
Wzorzec konstruktora ma to obejść. W tym scenariuszu tworzysz, PersonBuilderktóry zgłasza wyjątek, jeśli numer SSN nie jest ustawiony przed wywołaniem „kompilacji” w celu pobrania Personinstancji. Myślę, że tego rodzaju rzeczy są przesadne, ale to właśnie język Java promuje dla odpowiednich wzorców.
Sandy Chapman
8

Użyłbym tylko typów opakowań, jeśli musisz.

Korzystając z nich niewiele zyskujesz, poza tym, że są Objects.

I tracisz narzuty związane z użyciem pamięci i czasem spędzonym na boksowaniu / rozpakowywaniu.

jjnguy
źródło
4
możesz niewiele zyskać, ale też niewiele stracisz. chyba że biegasz na palmtopie z lat 90.
pstanton
Fakt, że są one Obiektami, daje im również znacznie więcej kontekstu i hermetyzacji niż zwykły prymityw. Więc możesz naprawdę wiele zyskać w zależności od tego, do czego służą te prymitywy i gdzie są używane.
aberrant80
4

Praktycznie spotkałem się z sytuacją, w której można wyjaśnić użycie klasy opakowującej.

Utworzyłem klasę usług, która miała longzmienną typu

  1. Jeśli zmienna jest typu long - gdy nie zostanie zainicjowana, zostanie ustawiona na 0 - będzie to mylące dla użytkownika, gdy zostanie wyświetlona w GUI
  2. Jeśli zmienna jest typu Long- gdy nie zostanie zainicjowana, zostanie ustawiona na null- ta wartość null nie pojawi się w GUI.

Dotyczy to Booleanrównież przypadków, w których wartości mogą być bardziej zagmatwane, gdy używamy prymitywów boolean(ponieważ wartość domyślna to fałsz).

Mohamed Afzal
źródło
3

Kolekcje są typowym przypadkiem dla prostych obiektów opakowania Java. Można jednak rozważyć nadanie Wrapperowi bardziej szczegółowego znaczenia w kodzie (obiekt wartości).

IMHO prawie zawsze istnieje korzyść z używania obiektów wartości, gdy sprowadza się to do czytelności i utrzymania kodu. Pakowanie prostych struktur danych wewnątrz obiektów, gdy mają one określone obowiązki, często upraszcza kod. Jest to bardzo ważne w projektowaniu opartym na domenie .

Istnieje oczywiście problem z wydajnością, ale zwykle go ignoruję, dopóki nie będę miał możliwości zmierzenia wydajności za pomocą odpowiednich danych i podjęcia bardziej ukierunkowanych działań na problematyczny obszar. Zrozumienie problemu z wydajnością może być również łatwiejsze, jeśli kod jest również łatwy do zrozumienia.

Mattias Holmqvist
źródło
3

Wydajność aplikacji zdominowanych przez obliczenia numeryczne może znacznie skorzystać na zastosowaniu prymitywów.

typy prymitywne, używa się operatora ==, ale w przypadku opakowania preferowanym wyborem jest wywołanie metody equals ().

„Typy prymitywne uważane za szkodliwe” ponieważ łączą ”semantykę proceduralną w jednolity model zorientowany obiektowo.

Wielu programistów inicjuje liczby na 0 (domyślnie) lub -1, aby to oznaczyć, ale w zależności od scenariusza może to być niepoprawne lub wprowadzające w błąd.

loknath
źródło
1

Jeśli chcesz korzystać z kolekcji, musisz użyć klas Wrapper.

Typy prymitywne są używane w tablicach. Ponadto, aby przedstawić dane, które nie mają żadnego zachowania, na przykład licznik lub warunek logiczny.

Od czasu autoboxingu granica „kiedy używać prymitywu lub opakowania” stała się dość niewyraźna.

Pamiętaj jednak, że opakowania to obiekty, więc masz wszystkie wymyślne funkcje Java. Na przykład, możesz użyć reflexion do tworzenia obiektów Integer, ale nie wartości int. Klasy opakowujące mają również metody, takie jak valueOf.

Tomek
źródło
Poza kolekcjami nie powinienem używać klas opakowujących? Co powiesz na użycie go do zwykłej deklaracji, takiej jak Integer i = new Integer (10); Czy dobrze jest to robić?
Gopi
autoboxing umożliwia wykonanie Integer i = 10;
Tom
1
Nie, Sri. Jeśli nie masz wymagania, żebym był przedmiotem, nie rób tego.
Matthew Flaschen
Autoboxing rozpakuje powyższe zadeklarowane i do int i = 10 lub Integer i = 10?
Gopi
3
int pi = new Integer (10); Pracuje. Liczba całkowita oi = 10; Pracuje. int ni = null; nie działa. LHS jest konwertowany na wszystko, czego wymaga RHS.
pstanton
0

Jeśli chcesz utworzyć typ wartości. Coś w rodzaju ProductSKU lub AirportCode.

Kiedy typ pierwotny (łańcuch w moich przykładach) definiuje równość, będziesz chciał zastąpić równość.

Chris Missal
źródło
5
string nie jest prymitywny
pstanton
2
nadal istnieją dobre powody, aby opakować typ wartości zawierający ciąg znaków jako obiekt bazowy.
Chris Missal
twoja odpowiedź po prostu nie ma sensu. nie jestem pewien, co mówisz. Zgadzam się jednak z twoim komentarzem, klasy opakowujące są dobrym pomysłem, jeśli poprawiają czytelność.
pstanton
Typy wartości lub obiekty wartości powinny być tworzone i niezmienne. Na przykład nie miałoby sensu tworzenie obiektu „CountryCode” takiego jak: new CountryCode („USA”), a następnie tworzenie innego obiektu w ten sam sposób, gdzie później będą one inne. Na początek są tylko sznurkami, ale mają za sobą znaczenie. Używając łańcuchów, możesz je modyfikować (dodając więcej danych itp.), Ale nie będą już równe. Zobacz ten artykuł, aby lepiej opisać to, co próbuję wyjaśnić :) Mam nadzieję, że to ma sens. C2.com/cgi/wiki?ValueObject
Chris Missal
0

Wartości pierwotne w Javie nie są obiektami. Aby móc manipulować tymi wartościami jako obiektami, pakiet java.lang udostępnia klasę opakowania dla każdego pierwotnego typu danych.

Wszystkie klasy Wrapper są ostateczne. Obiekty wszystkich klas opakowania, które można zainicjować, są niezmienne, co oznacza, że ​​nie można zmienić wartości obiektu opakowania.

Chociaż klasa void jest uważana za klasę opakowania, ale nie zawija żadnych wartości pierwotnych i nie można jej zainicjować. Nie ma konstruktora publicznego, po prostu oznacza obiekt klasy reprezentujący słowo kluczowe void.

Ahmad Sayeed
źródło