Co zyskujemy pisząc
List<?> myList = new ArrayList<?>();
zamiast
ArrayList<?> myList = new ArrayList<?>();
Co zyskujemy pisząc
List<?> myList = new ArrayList<?>();
zamiast
ArrayList<?> myList = new ArrayList<?>();
W pierwszej opcji nie ograniczasz się do jednej implementacji.
Zauważ że List
to jest interfejs. Pisząc z lewej strony List
możesz potem do tej zmiennej podstawić inna implementacje List
, a nie tylko ArrayList
.
Alexandr.r napisał(a):
Co zyskujemy pisząc
List<?> myList = new ArrayList<?>();
zamiast
ArrayList<?> myList = new ArrayList<?>();
W takiej formie właściwie nie ma różnicy. Większość pisze to co jest krótsze.
Natomiast podczas deklarowania parametrów, lepiej zadeklarować przyjmowanie List<>
zamiast ArrayList<>
, by móc przekazać polimorficznie dowolną implementację List
.
Riddle napisał(a):
W takiej formie właściwie nie ma różnicy. Większość pisze to co jest krótsze.
Ogólnie zgoda, natomiast jest jeden mały fakt: zapis ogólny oznacza, że nie można wykorzystać metod właściwych dla ArrayList
.
Inna sprawa, że pisanie tego, co jest krótsze nie zawsze jest dobre. Czasami lepiej napisać ArrayList<?> myList = new ArrayList<>(capacity)
by dać światu znać, że kod został napisany z myślą o ArrayList
.
wartek01 napisał(a):
Riddle napisał(a):
W takiej formie właściwie nie ma różnicy. Większość pisze to co jest krótsze.
Ogólnie zgoda, natomiast jest jeden mały fakt: zapis ogólny oznacza, że nie można wykorzystać metod właściwych dla
ArrayList
.
Jeśli chcemy wykorzystać metod właściwych dla ArrayList
, to całe to pytanie nie ma sensu.
Let me Chat GPT that for you:
@0xmarcin: Nie lubię tych odpowiedzi z ChatGPT, bo one się dzielą na dwie kategorię.
Zadajesz pytanie, i bot odpowiada:
Skąd pytający ma wtedy wiedzieć czy chat odpowiedział coś poprawnie czy jakiś bezsens?
W programowaniu warto określać to co myślisz najlepiej jak się da. Jeśli w danym miejscu nie dbasz o to jakiej implementacji listy używasz to użyj generycznego typu. W praktycznie każdym wypadku tak jest, bo wybór implementancji jest zazwyczaj podyktowany charakterystyką wydajnościową i jej użycie jest takie samo niezależnie od implementacji pod spodem
Alexandr.r napisał(a):
Co zyskujemy pisząc
List<?> myList = new ArrayList<?>();
zamiast
ArrayList<?> myList = new ArrayList<?>();
Tak na prawdę powinieneś zrobić krok wstecz i zapoznać się z polimorfizmem, bo na tym polega cała sztuczka. Bez tego ani rusz.
Alexandr.r napisał(a):
Co zyskujemy
Dodatkowy punkt na rozmowie kwalifikacyjnej.
No właśnie chodziło mi o zysk z tego tytułu - pisząc List<?> list = new ArrayList<>()
zyskujesz gwarancję, że nikt nie będzie korzystał z metod właściwych dla ArrayList
. Może to mieć znaczenie przy np. refaktorze.
wartek01 napisał(a):
No właśnie chodziło mi o zysk z tego tytułu - pisząc
List<?> list = new ArrayList<>()
zyskujesz gwarancję, że nikt nie będzie korzystał z metod właściwych dlaArrayList
. Może to mieć znaczenie przy np. refaktorze.
Weeeź, daj spokój, to jest jakiś frazes.
Nie jest żadna "gwarancja", jak ktoś będzie chciał użyć metod z ArrayList
to po prostu zmieni zadeklarowany typ i tyle. Poza tym, nawet jeśli - to po co utrudniać programistom pracę. Jeśli ktoś chce użyć metod z ArrayList
to niech sobie użyje, jezu. Po to są.
Jak już piałem, przyjęcie w argumencie List<>
nawet ma sens, ale w deklaracji raczej mały. Od javy 11tki i tak ludzie piszą var
, a on implicitly wsadzi ArrayList
. Jak mówiłem - większość pisze List
bo jest krótsze i nikt się nad tym nie zastanawia dwóch sekund.
Alexandr.r napisał(a):
Co zyskujemy pisząc
List<?> myList = new ArrayList<?>();
zamiast
ArrayList<?> myList = new ArrayList<?>();
List
to interface, który określa co robi implementujący go obiekt. ArrayList
to jedna wielu możliwych implementacji tego interface'u, która jest dość szybka w dodawaniu elementów do końca, ale bardzo słaba w zmianach wewnątrz. Jeżeli w którymś momencie stwierdzisz, że większość operacji to dodawanie elementów do początku i usuwanie ich z końca, to możesz sobie bezkarnie podmienić ArrayList
na LinkedList
. Całość będzie robić dalej to samo, ale w niektórych przypadkach szybciej.
Dodatkowo, jeżeli będziesz np. potrzebował umieścić tę listę na zewnętrznym serwerze, to możesz sobie napisać własną implementację, która zamiast trzymać dane w pamięci, umieści i pobierze je z np. Redisa.
piotrpo napisał(a):
Alexandr.r napisał(a):
Co zyskujemy pisząc
List<?> myList = new ArrayList<?>();
zamiast
ArrayList<?> myList = new ArrayList<?>();
List
to interface, który określa co robi implementujący go obiekt.ArrayList
to jedna wielu możliwych implementacji tego interface'u, która jest dość szybka w dodawaniu elementów do końca, ale bardzo słaba w zmianach wewnątrz. Jeżeli w którymś momencie stwierdzisz, że większość operacji to dodawanie elementów do początku i usuwanie ich z końca, to możesz sobie bezkarnie podmienićArrayList
naLinkedList
. Całość będzie robić dalej to samo, ale w niektórych przypadkach szybciej.
Dodatkowo, jeżeli będziesz np. potrzebował umieścić tę listę na zewnętrznym serwerze, to możesz sobie napisać własną implementację, która zamiast trzymać dane w pamięci, umieści i pobierze je z np. Redisa.
Nie ma znaczenia czy List
to interfejs, klasa abstrakcyjna czy klasa bazowa w tym wypadku.
Użycie List
ma tak na prawdę tylko dwie sensowne konsekwencje:
List
w bibliotece w .jar
, to nie musimy go recompilować jeśli chcemy zmienić wystawioną implementacjęList
, to możemy ją wywołać z dwoma parametrami na raz w jednym programie (np x(new ArrayList())
oraz x(new LinkedList())
)Poza tymi dwoma rzeczami, nie ma znaczenia której użyjemy. Dostajesz tutaj odpowiedzi z List
możesz bezkarnie zmieniać implementacje, ale tak samo możesz z ArrayList
- zmiana ArrayList
na LinkedList
dla ogarniętej osoby to jest max. 5 sekund.
@Riddle: No trochę jednak ma znaczenie. Abstrakcje na zewnątrz powinny być jak najmniej szczegółowe, żeby nie ograniczać niepotrzebnie możliwości manipulowania implementacją. Ale faktycznie w przypadku tego konkretnego tematu jest to raczej pusta dyskusja.
piotrpo napisał(a):
@Riddle: No trochę jednak ma znaczenie. Abstrakcje na zewnątrz powinny być jak najmniej szczegółowe, żeby nie ograniczać niepotrzebnie możliwości manipulowania implementacją. Ale faktycznie w przypadku tego konkretnego tematu jest to raczej pusta dyskusja.
Nie ma. Przypisuje się zbyt duże znaczenie temu czy zadeklarujesz zmienną jako List
czy ArrayList
. Intencja za nimi jest słuszna -> jeśli chcemy iść w DI lub O/C, ale sama deklaracja nie ma znaczenia.
Jeśli wszystkie argumenty tutaj wziąć na serio (czyli te wasze "gwarancje", intencje, zmiany implementacji) to powinniśmy deklarować wszystkie te zmienne jako Collection<>
albo Enumerable<>
- jeśli słuchać wymienionych rad tutaj, deklaracja jako Collection<>
dałaby jeszcze większą wolność w zmianie implementacji - czemu nikt tego nie robi? No głównie dlatego że List<>
szybciej się pisze, i to jest główny powód czemu jest stosowane.
Jeśli typ interfejsu byłby dłuższy niż imeplementacja (np ListInterface i = new List<>();
) to gwarantuje Ci że 99% programistów i tak pisałoby List l = new List();
.
Riddle napisał(a):
Weeeź, daj spokój, to jest jakiś frazes.
Żaden frazes, a częsta praktyka.
Masz rację w przypadku szczególnym, tj. takim, że w przypadku list to nie ma żadnego znaczenia. Wynika to z prostego faktu - tych metod się dzisiaj prawie nie używa. Ba, dzisiaj bardzo rzadko używa się dostępu przez indeks, jedynie z add()
i stream()
czy forEach()
.
Natomiast w przypadku ogólnym, takie ukrywanie konkretnej implementacji może mieć znaczenie - i jeśli ktoś próbuje to zmienić to jest to niezły sygnał ostrzegawczy przy code review.
jeśli słuchać wymienionych rad tutaj, deklaracja jako Collection<> dałaby jeszcze większą wolność w zmianie implementacji - czemu nikt tego nie robi? No głównie dlatego że List<> szybciej się pisze, i to jest główny powód czemu jest stosowane.
No ogólnie gdyby ludzie tak pisali, to łamaliby zasadę Liskowej - i pewnie część z nich poniosłaby konsekwencje.
Taki prosty test:
Collection<Integer> c = getImplementation();
c.add(1);
c.add(1);
assertEquals(2, c.size());
wywali się w zależności od tego, co zwróci getImplementation()
. Gdy zamienisz Collection
na List
to jeśli nie użyjesz jakiejś egzotycznej (tj. popapranej) implementacji to ci zadziała.