2015-04-20 31 views
6

Zróbmy przykład, aby było łatwiej. Buduję listę, którą konstruktor pobiera integer i List<Integer>. Moja lista zawiera wszystkie elementy z podanej listy pomnożone przez integer. Moja lista nie przechowuje nowe elementy, ale obliczyć je na bieżąco:Jak zwrócić obiekt z wieloma typami?

class MyList extends AbstractList<Integer> implements RandomAccess { 
    private final int multiplier; 
    private final List<Integer> list; 

    public MyList(int multiplier, List<Integer> list) { 
     this.multiplier = multiplier; 
     this.list = list; 
    } 

    @Override 
    public Integer get(int index) { 
     return list.get(index) * multiplier; 
    } 

    @Override 
    public int size() { 
     return list.size(); 
    } 
} 

Wtedy możemy nazwać new MyList(3, list) z list = [0, 1, 2, 3] dostać [0, 3, 6, 9].

Chciałbym ograniczyć programistę, aby dać konstruktorowi MyList listę, która jest również RandomAccess, aby upewnić się, że nie zepsuje osiągów.

Próbowałem zmienić konstruktora z:

public <E extends List<Integer> & RandomAccess> MyList(int multiplier, E list) 

MyList nie jest problem, ale teraz nie możemy wywołać konstruktora bez używania implementację zarówno List<Integer> i RandomAccess jak ArrayList<Integer>. Więc ktoś, kto ma tę listę: List<Integer> list = new ArrayList<>(); nie może zrobić new MyList(3, list); (ponieważ jest zadeklarowana z List<Integer> zamiast ArrayList<Integer>).

Innym rozwiązaniem mam jest to jedno:

public MyList(int multiplier, List<Integer> list) { 
     if(!(list instanceof RandomAccess)) { 
      // Do something like log or throw exception 
     } 
     this.multiplier = multiplier; 
     this.list = list; 
    } 

Ale teraz nie mogę sprawdzić w czasie kompilacji, jeśli lista implementuje RandomAccess i muszę korzystać instanceof i nienawidzę tego.

Jestem całkiem pewien, że istnieje lepszy sposób, ale co to jest?

+0

Spróbuj użyć | (lub operator) zamiast operatora & (i operatora). Nie jestem pewien, czy to zadziała, ale warto spróbować. – MLavrentyev

+0

Więc jeśli chcesz ograniczyć programistę do 'RandomAccess' _", aby upewnić się, że nie zepsuje on osiągów "_, to dlaczego nadal chciałbyś zezwolić im na wywołanie twojego konstruktora za pomocą' List', co oczywiście nie implementuje 'RandomAccess'? Czy to tylko ze względów kosmetycznych? –

+0

Muszę wywołać 'List.get()'. – Happy

Odpowiedz

1

Można przyjąć rozwiązanie wykorzystywane przez Collections.unmodifiableList. Zamiast publicznego konstruktora, mamy statyczną metodę, która zwraca jedną z dwóch implementacji, jedną wykonującą RandomAccess, a drugą nie.

Oto kod dla Collections.unmodifiableList.

public static <T> List<T> unmodifiableList(List<? extends T> list) { 
    return (list instanceof RandomAccess ? 
      new UnmodifiableRandomAccessList<>(list) : 
      new UnmodifiableList<>(list)); 
} 

Wiem, że powiedziałeś, że nie lubisz używać instanceof. Ja też nie, ale czasami jest to najlepsze.

Należy zauważyć, że rozwiązanie z użyciem konstruktora

public <E extends List<Integer> & RandomAccess> MyList(int multiplier, E list) 

nie jest tak brzydki, że zmusza programistę do oddania (np do ArrayList), ale to nie będzie działać właściwie. Na przykład, jeśli list jest instancją Collections$UnmodifiableRandomAccessList, nie byłoby nawet możliwe rzutowanie jej na typ implementujący zarówno List, jak i , ponieważ Collections$UnmodifiableRandomAccessList jest prywatny.

0

Jeśli klasa potrzebuje listy dostępu losowego, a następnie klasa powinien sobie z tym poradzić i nie naciskać potrzeby Twojego klasa jest na rozmówcy.Ponadto, byłoby prościej wykonać mnożenie w konstruktorze - trzeba to zrobić w pewnym momencie, ale robi to na początku oznacza, że ​​można wyrzucić wiele kodu:

class MyList extends ArrayList<Integer> { 

    public MyList(int multiplier, List<Integer> list) { 
     for (Integer i : list) 
      add(i * multiplier); 
    } 
} 

To wszystko, czego potrzebujesz, i jest bezpieczniej: z twoim unieruchomieniem zarówno twoja lista jak i dzwoniący mają odniesienie do listy. Jeśli po wywołaniu konstruktora wywołanie zmieni listę, inny kod użyty na liście mnożonej niespodziewanie zobaczy zmiany wartości na liście.

+0

Twoje założenie, że "musisz to zrobić w pewnym momencie" może być fałszywe. Jest całkiem możliwe, że jest to lista wielu przedmiotów, w których bardzo niewielu jest faktycznie dostępnych (stąd potrzeba dostępu losowego). – sprinter

+0

@sprinter Właśnie wykonałem test wydajności: uruchomienie tego kodu z listą wejściową o wielkości 10 losowych liczb zajęło mniej niż 5 mikrosekund (na przeciętnym komputerze, po rozgrzewce), aby utworzyć nową listę pomnożonych liczb - to 0.0000005 sekund na element. Nie wiem jak wy, ale mogę żyć z tym "uderzeniem wydajności", aby oczyścić cały ten kod. – Bohemian

+0

Przez wiele przedmiotów miałem na myśli wiele milionów. Szczerze mówiąc, jeśli masz 10 pozycji na liście, struktura danych nie ma większego znaczenia. – sprinter

1

Sugerowałbym użycie instanceof. W rzeczywistości to właśnie dokumentacja RandomAccess proponuje:

Generic algorytmy lista zachęca się, by sprawdzić, czy dana lista jest instancją ten interfejs przed zastosowaniem algorytmu, który dawałaby słabe wyniki, jeśli były stosowane wobec sekwencyjną listę dostępową i zmienić ich zachowanie, jeśli jest to konieczne, aby zagwarantować akceptowalną wydajność.

Twój konstruktor może potencjalnie mieć dwie implementacje. Jeśli RandomAccess jest realizowany następnie przechowuje odniesienie do List inaczej tworzy nowy ArrayList i kopie wszystkich elementów IT:

class MyList { 
    private final int multiplier; 
    private final List<Integer> list; 

    public MyList(int multiplier, List<Integer> list) { 
     this.multiplier = multiplier; 
     if (list instanceof RandomAccess) 
      this.list = list; 
     else 
      this.list = new ArrayList<>(list); 
    } 

    public int get(int index) { 
     return multiplier * list.get(index); 
    } 
} 
+0

Twoja odpowiedź i pbabcdefp są dla mnie wystarczająco dobre. Przechowuję niektóre dane, jeśli tylko tego potrzebuję. Chciałbym więc uniemożliwić programistom wykorzystanie listy, która implementuje RandomAccess. Czy istnieje programowy sposób na zrobienie tego? Czy jedyną alternatywą, którą mam, jest javadoc? – Happy