2009-04-26 13 views
6

W moim projekcie Java mam wektor różnych typów traderów. Te różne typy handlowców są podklasami klasy Trader. W tej chwili mam metodę, która bierze Tradera jako argument i przechowuje go 50 lub więcej razy w wektorze. Mam problem, ponieważ przechowywanie tego samego obiektu 50 razy to przechowywanie 50 referencji tego samego obiektu. Muszę przechowywać 50 kopii obiektu. Badałem na temat implementacji Clone, ale nie chcę, aby programiści definiowali typ Tradera, który musiałby się martwić o to, aby ich klasy były klonowalne. Ponadto, jak zauważył this page, implementacja klona stwarza wiele problemów. Nie sądzę, by konstruktor kopii działał, ponieważ jeśli zdefiniowałbym go w klasie Trader, nie znałby rodzaju Tradera, który kopiował i po prostu zrobił zwykłego Tradera. Co mogę zrobić?Czy są jakieś alternatywy dla wdrażania Clone w Javie?

Edytuj: Naprawdę nie chcę tworzyć dokładnych kopii danego obiektu. Naprawdę staram się dodać do wektora pewną liczbę traderów. Problem polega na tym, że użytkownik musi określić w argumencie, który typ Maklera chce dodać. Oto przykład tego, co próbuję zrobić (chociaż moja składnia jest całkowicie wyimaginowane)

public void addTraders(*traderType*) 
{ 
    tradervect.add(new *traderType*()) 
} 

Jak mogę osiągnąć coś takiego w Java?

Odpowiedz

2

Wystarczy dodać abstrakcyjną metodę kopiowania. Można używać typów zmiennych kowariantnych, aby typ pochodny był określony w celu zwracania instancji pochodnej, która może ale nie musi być ważna.

public interface Trader { 
    Trader copyTrader(); 
    ... 
} 


public final class MyTrader implements Trader { 
    MyTrader copyTrader() { 
     return new MyTrader(this); 
    } 
    ... 
} 

Czasami warto ogólnie sprawa z kolekcją typu pochodzącego z Trader że potrzebuje sklonować, a następnie zwrócić się poprawnie wpisane kolekcję. W tym celu można użyć rodzajowych w idiomatycznym sposób:

public interface Trader<THIS extends Trader> { 
    THIS copyTrader(); 
    ... 
} 


public final class MyTrader implements Trader<MyTrader> { 
    public MyTrader copyTrader() { 
     return new MyTrader(this); 
    } 
    ... 
} 
+0

Nie widzę sposobu, w jaki można uczynić ogólne klasy Trader interfejsem, ponieważ klasa Trader ma metody, które muszą być dostępne. Utworzenie interfejsu oznaczałoby, że wszystkie te metody niskiego poziomu nie pozwoliłyby na zdefiniowanie tych metod w klasie Trader. –

+0

To, czy Trader jest abstrakcyjną klasą, czy interfejsem, jest nieistotne. Ogólnie interfejs jest preferowany w stosunku do klasy abstrakcyjnej. Również preferuj delegację do dziedziczenia. –

0

Jedna opcja: Jeśli możesz uczynić obiekty serializowalnymi, możesz serializować, a następnie deserializować, aby utworzyć kopię, podobną do tego, co dzieje się przy przekazywaniu obiektu przez RMI.

Szybka metoda kopiowania:

public MyObject copy() { 
    ObjectOutputStream oos = null; 
    ObjectInputStream ois = null; 
    try { 
     ByteArrayOutputStream bos = new ByteArrayOutputStream(); 
     oos = new ObjectOutputStream(bos); 
     oos.writeObject(this); 
     oos.flush(); 
     ByteArrayInputStream bin = 
      new ByteArrayInputStream(bos.toByteArray()); 
     ois = new ObjectInputStream(bin); 
     return (MyObject)ois.readObject(); 
    } catch(Exception e) { 
     return null; 
    } finally { 
     try { 
      oos.close(); 
      ois.close(); 
     } catch (Exception e) { 
      return null; 
     } 
    } 
} 
+0

Dlaczego upadek? Jest to odpowiedni sposób na odczytywanie złożonego obiektu. Zarządzanie zasobami jest jednak całkowicie zepsute. –

+0

Czy to jedyne rozwiązanie? Nie mogę uwierzyć, że taka prosta operacja spowodowałaby tyle zamieszania. Co się stanie, jeśli zaimplementuję klon, aby umożliwić kopiowanie. Czy to byłoby możliwe? –

+0

@Tom: Nie jestem pewien, ale użycie serializowania/deserializacji do klonowania jest tak subtelne, jak używanie memcpy zamiast konstruktorów kopiowania w C++. – Uri

-2

Jednym ze sposobów jest, aby ostateczna klasa jak własnym String Java, który sprawi, że wszelkie zmiany do obiektu klasy Trader, aby utworzyć nową kopię w pamięci , ale uniemożliwi to jej podklasę.

Innym (lepszym) sposobem jest użycie metody fabrycznej do tworzenia i kopiowania objetków dla Tradera, co oznacza, że ​​nie wolno zezwalać na użycie domyślnego konstruktora, tj. Uczynić go prywatnym. W ten sposób możesz kontrolować liczbę wystąpień klasy. Zobacz http://en.wikipedia.org/wiki/Factory_method

public class Trader { 

    /* prevent constructor so new cant be used outside the factory method */ 
    private Trader() { 
    } 

    /* the factory method */ 
    public static Trader createTrader(int whatKindOfTrader) { 

     switch (whatKindOfTrader) { 
     case 0: 
      return new Trader1(); // extends Trader 
     case 1: 
     default: 
      return new Trader2(); // extends Trader 
     } 
     return new Trader3(); // or throw exception 
    } 
} 

Można nawet określić inną przeciążone metody, ani drugi argument, że trwa jeden z handlowców i kopiuje go na nowy, a tym samym zastąpienie klona. Btw, możesz chcieć zastąpić metodę clone() i wrzucić CloneNotSupportedException, aby zapobiec domyślnemu klonowaniu obiektów.

+0

Masz na myśli niezmienną, a nie tylko ostatnią klasę. Niezmienność prawdopodobnie trochę za dużo dla obiektu "przedsiębiorcy". –

+0

Nie byłem niezmienny, ale ostateczny. Jest różnica i osobiście nigdy jej nie użyłbym. Ale metoda Factory to najlepsza praktyka i jest to powszechny wzorzec projektowania Btw, niezmienny obiekt jest po prostu nieprzydatny w tym przypadku. – Azder

2

Jestem trochę niejasny, dlaczego chciałbyś przechowywać 50 lub więcej klonów tego samego obiektu, chyba że oryginalny sprzedawca służy jako prototyp (patrz wzór) dla późniejszych handlowców.

Jeśli chcesz wykonać dokładną kopię obiektu, musisz wziąć pod uwagę kwestię polimorfizmu. Jeśli podklasy danej klasy mogą dodawać członków stanu, masz już wystarczająco dużo bólu głowy z funkcjami takimi jak equals i compareTo, ten klon to kolejny przypadek, w którym musisz wymagać specjalnej obsługi.

Nie zgadzam się, że klon jest zawsze zły, czasami jest potrzebny. Jednak w sytuacjach podklasowania wiele rzeczy staje się trudnych.

Polecam przeczytać (kiedy masz szansę) "Efektywna Java" Blocha, która obejmuje wiele jego tematów. Bracha uważa, że ​​nie jest dobrym pomysłem, aby inni mogli rozszerzyć twoje zajęcia, a jeśli to zrobisz, musisz bardzo dobrze udokumentować, co będą musieli zrobić i mieć nadzieję, że będą postępować zgodnie z twoimi instrukcjami. Naprawdę nie ma sposobu na ominięcie tego. Możesz również uczynić swoich Inwestorów niezmiennymi.

Należy użyć narzędzia Klonuj normalnie i wyraźnie zaznacz w nagłówku klasy, że każdy dziedziczący po Traderze i dodający elementy stanu musi implementować metody X (określić, które).

Znajdowanie sztuczek, które pomijają Klonowalny i wciąż Klon, nie rozwiąże problemu dziedziczenia. Tutaj nie ma srebrnej kuli.

+0

Twoja odpowiedź mnie zastanowiła. Zobacz edytowaną przeze mnie edycję, aby uzyskać więcej informacji.:) –

+0

Czekaj, czy zasadniczo mówisz, że jeśli X rozszerza Tradera, a ktoś przekazał instancję X do swojej funkcji, chcesz otrzymać kolejne nowe X? – Uri

+0

Jeśli chcesz ten problem, należy użyć (straszne) getClass.newInstance idiom. traderType.getClass() newInstance() Musisz upewnić się, że nie zawsze jest konstruktor publiczny i prawdopodobnie chcesz jakieś metody init metodę, którą zawsze możesz wywołać, aby wprowadzić ją w legalny stan. – Uri

0

Uri ma rację, polimorfizm z państwem otwiera wielką puszkę robaków.

Uważam, że podklasowanie Klonowalny i nadpisujący klon() jest prawdopodobnie najprostszą metodą. Możesz, jak sądzę, dokonać kowariantu typu zwrotu.