2012-06-07 19 views
17

Powiedzmy mam dwóch konstruktorów w klasie:Java - Jak radzić sobie z wymazywaniem typów w konstruktorach?

public User (List<Source1> source){ 
... 
} 

public User (List<Source2> source) { 
... 
} 

powiedzmy, że oba te konstruktorów te same informacje o użytkowniku i są równie ważne sposoby skonstruować użytkownika dla różnych zastosowań.

W Javie nie można tego zrobić z powodu usunięcia typu - Java nie zaakceptuje dwóch konstruktorów, które mają jako parametry Listę <? >.

A więc, jak to obejść? Co to jest rozwiązanie, które nie jest przesadą, ale nadal respektuje podstawowe OO? Wydaje się błędne, aby skonstruować metodę fabryczną lub inny interfejs wokół tego tylko dlatego, że Java nie ma silnej obsługi generycznych.

Oto możliwości mogę myśleć:

1) zaakceptować List<?> jako parametr do konstruktora i analizować w konstruktorze, jaki rodzaj logiki trzeba, czy wyjątek, jeśli nie jest którykolwiek z akceptowane typy.

2) Utwórz klasę, która akceptuje obie listy, konstruuje odpowiedni obiekt użytkownika i zwraca go.

3) Utwórz owijki wokół List<Source1> i List<Source2>, które można zamiast tego przekazać do konstruktora użytkownika.

4) Podklasa tego faceta z dwiema klasami, w którym cała funkcjonalność jest dziedziczona z wyjątkiem konstruktora. Konstruktor jednego akceptuje Source1, drugi akceptuje Source2.

5) Owiń tego faceta z budowniczym, gdzie są dwie różne metody budowania dla dwóch różnych źródeł danych do utworzenia instancji.

Moje pytania są następujące:

1) Czy trzeba to zrobić wadę z Java lub umyślne decyzji projektowych? Jaka jest intuicja?

2) Które rozwiązanie jest najsilniejsze pod względem utrzymania dobrego kodu bez wprowadzania zbędnej złożoności? Czemu?

To pytanie jest podobne: Designing constructors around type erasure in Java, ale nie opisuje szczegółów, tylko sugeruje różne rozwiązania.

+8

po co to robić, dlaczego nie wpisać parametrize całej klasy? lub po prostu stworzyć fabrykę, która w każdym razie jest dobrym wzorem. –

+0

Możesz napisać jedną funkcję, a następnie przetestować typy list na początku funkcji, a następnie wpisać rzutowanie na podstawie typu ... lub użyć lepszego języka – NKamrath

+0

... lub możesz korzystać z realnych typów, takich jak tablice . –

Odpowiedz

8

Zwykle podejście jest użycie factory methods:

public static User createFromSource1(List<Source1> source) { 
    User user = new User(); 
    // build your User object knowing you have Source1 data 
    return user; 
} 

public static User createFromSource2(List<Source2> source) { 
    User user = new User(); 
    // build your User object knowing you have Source2 data 
    return user; 
} 

Jeśli tylko chcą budowy przy użyciu Source1 lub Source2 (czyli nie ma domyślnego konstruktora), po prostu ukryć konstruktora, zmuszając klientów Aby użyć swoich metod fabrycznych:

private User() { 
    // Hide the constructor 
} 

Ten problem powstaje, ponieważ nie można inaczej nazwać konstruktorów, w jaki sposób można przezwyciężyć to jeśli to były normalne metody. Ponieważ nazwy konstruktorów są naprawiane jako nazwa klasy, ten wzór kodu jest tylko sposobem na rozróżnienie, a następnie daje ten sam typ wymazania.

+0

Pytający mówi: "Wydaje się błędne skonstruowanie metody fabryki lub innego interfejsu wokół tego tylko dlatego, że Java nie ma silnej obsługi generycznych." ... ale jest to tylko jeden sposób, w jaki konstruktory Java są znacznie bardziej ograniczone niż statyczne metody fabryczne. (Zobacz także: Efektywna Java i http://stackoverflow.com/questions/4029622/static-factory-method-for-constructors) –

1

1: Zachowanie kompatybilności wstecznej ze skasowaniem.

2: Czy twoja klasa może używać generycznych? Coś takiego:

public class User<T> { 
    private List<T> whatever; 
    public User(List<T> source){ 
     .... 
    } 
} 

Nie jestem pewien, czy to jest to, co rozumie się przez (2)

0
public class User<T> { 

    public User(List<T> source){ 

    } 

} 

lub prawdopodobnie lepiej:

public class User<T extends SomeTypeOfYours> { 

    public User(List<T> source){ 

    } 
} 

Wheer SomeTypeOfYours jest super-type z Source1 i Source2.

0

Podobał mi się ogólny pomysł na fabrykę lub generowanie użytkownika (zgodnie z sugestią @Markus Mikkolainen), ale jedną z możliwych alternatyw jest przekazanie klasy listy jako drugiego argumentu i włączenie jej, np.

public User<List<?> source, Class<?> clazz) { 
    switch(clazz) { 
     case Source1.class: initForSource1(); break; 
     case Source2.class: initForSource2(); break; 
     default: throw new IllegalArgumentException(); 
    } 
} 

<?> może być coś innego, jeśli istnieje jakiś wspólny przodek klasy. Mogę sobie wyobrazić wiele przypadków, w których jest to kiepski pomysł, ale kilka przypadków, w których może to być dopuszczalne.

1

Podstawowy problem polega na tym, że język został zaprojektowany (z ustaloną nazwą konstruktora) przed istniejącymi rodzajami, więc nie może obsłużyć kolizji z powodu usunięcia typu, co normalnie byłoby obsługiwane przez zmianę nazw metod w celu ich rozróżnienia.

One „obejście”, bez uciekania się do metod fabrycznych, jest dodanie innego non-wpisany parametr umożliwienie kompilator je rozróżnić:

public User(List<Source1>, Source1 instance) { 
    // ignore instance 
} 

public User(List<Source2>, Source2 instance) { 
    // ignore instance 
} 

To jest trochę kulawy choć, jak można zastąpić te dodatkowe parametry z niczym (np. Integer i String lub po prostu jeden z nich pomija drugi parametr) i nadal będzie działać. Ponadto dodatkowy parametr jest ignorowany - istnieje tylko w celu odróżnienia konstruktorów. Mimo to umożliwia działanie kodu bez dodawania żadnych dodatkowych metod lub specjalnego kodu.

Powiązane problemy