2011-11-07 13 views
6

Jest to nieprzyjemny problem i może być tak, że projekt jest po prostu zły.Odziedziczyłem niektóre złe generics Java

Zapisywanie zestawu prostych elementów wykresów (wykres kołowy, pasek &) i dławię się niektórymi rzeczami generycznymi. Wcześniej jestem pewien, że istnieje wiele API Java do robienia dokładnie tego, co próbuję tutaj zrobić (wykresy/raporty/itp.), Jednak jestem zainteresowany tym jako ogólnym problemem generycznym; fakt, że zawiera on wykresy & komponentów raportowania jest banalne.

Każdy wykres dziedziczy z klasy generycznej streszczenie bazowej Chart:

public abstract class Chart<T extends ChartComponent> 
{ 
    private List<T> components; 

    // ...rest of the Chart class 
} 

Powodem mamy T extends ChartComponent dlatego każdy wykres podklasa będzie zawierał 1+ tzw wykres komponentów (bary, linie kliny pie, itp):

public abstract class ChartComponent 
{ 
    private Color color; 

    // .. rest of ChartComponent class 
} 

public class PieWedge extends ChartComponent 
{ 
    double wedgeValue; 

    // ... rest of PieWedge class 
} 

Umieszczenie ten projekt razem:

public class PieChart extends Chart<PieWedge> 
{ 
    // ... thus its list of ChartComponents is actually a List<PieWedge> 
} 

W ten sposób PieChart nie jest generyczna (ani nie powinna być) i zawsze jest typu Chart<PieWedge>.

Poprzednio miałem tę samą konfigurację dla wykresów słupkowych i liniowych, które zdefiniowano odpowiednio jako BarChart extends Chart<BarGroup> i LineChart extends Chart<Line> (ponieważ wykres słupkowy składa się z 1+ grup słupków, a wykres liniowy składa się z 1+ linii).

Teraz chcę jeszcze bardziej wykreślić wykresy słupkowe i liniowe. Oba te wykresy są faktycznie narysowane na wykresie kartezjańskim (x, y) z osiami xi y; jest to przeciwieństwo wykresu kołowego, który nie jest wykreślony na żadnej z takich osi.

Idealnie, chciałem stworzyć nową klasę abstrakcyjną nazwie CartesianChart który przedłużył Chart, a następnie mieć BarChart i LineChart zarówno przedłużyć CartesianChart. Ten nowy CartesianChart wprowadziłby nowe właściwości (xAxisLabel, gridTurnedOn itd.), Które logicznie stosują się do wykresów słupkowych/liniowych, ale nie do wykresów kołowych.

Ponadto, aby ograniczyć CartesianChart tak, że może mieć tylko chartComponents typu BarGroup lub Line (a nie PieWedge), ja jak tworzyć nowy rodzaj komponentu wykres jak CartesianComponent extends ChartComponent i wtedy BarGroup/Line przedłużenie tego. Dzięki temu można by uniknąć takiego kodu z kompilacji:

LineChart lineChart = new LineChart(); 
lineChart.addLine(new PieWedge()); 

Od Line rozciąga CartesianComponent, ale PieWedge rozciąga się tylko ChartComponent. Tak więc, zanim się do mojego problemu mamy następującą hierarchię dziedziczenia:

Chart 
    CartesianChart 
     BarChart 
     LineChart 
    PieChart 

ChartComponent 
    CartesianComponent 
     BarGroup 
     Line 
    PieWedge 

PieChart extends Chart<PieWedge> 

CartesianChart extends Chart<CartesianComponent> 

BarGroup extends CartesianComponent 
Line extends CartesianComponent 

BarChart extends CartesianChart<BarGroup> 
LineChart extends CartesianChart<Line> 

Problem z tej konfiguracji jest to, że zarówno BarChart i LineChart daje błąd kompilatora narzekają, że CartesianChart nie jest nazwą rodzajową. To ma sens, ale nie jestem pewien, co mogę zrobić, aby to naprawić!

Gdy próbuję na nowo zdefiniować CartesianChart:

public abstract class CartesianChart<T extends CartesianComponent> extends Chart<CartesianComponent> 
{ 
    // ... 
} 

otrzymuję „Niezgodność typu” błędy kompilatora wszystko przez moją bar/linia kodu wykresu. W każdym wystąpieniu tego błędu stwierdza on, że oczekuje argumentów typu List<CartesianComponent>, ale zamiast tego stwierdził, że są one nieodpowiednimi zamiennikami.

Mamy nadzieję, że jest to szybka poprawka gdzieś w definicji klasy CartesianChart i/lub CartesianComponent. W przeciwnym razie może zajść konieczność przeprojektowania całej biblioteki wykresów. Tak czy inaczej, jestem zainteresowany w dowolnych i wszystkich sugestii wyjątkiem z nich jak „Hej, dlaczego nie można po prostu spróbować JFreeCharts lub ...”. Ponownie, interesuje mnie tutaj rozwiązanie, ponieważ dotyczy ono rozwiązywania szerokiego zakresu podobnych problemów generyków; fakt, że wymaga to raportowania/tworzenia wykresów, jest banalny.

Z góry dziękuję za wszelką pomoc!

+0

Co próbujesz osiągnąć, generując 'Chart' generic? Innymi słowy, dlaczego nie zdefiniować po prostu komponentów "Chart Chart {private List ; } '? –

+0

'publiczna klasa abstrakcyjna CartesianChart przedłuża Wykres '? – digitaljoel

+0

Dziękuję za sugestie tutaj. Spróbuję tutaj obu twoich sugestii. Proszę zobaczyć mój komentarz poniżej odpowiedzi na @ nicholas.hauschild o moim obawie, że rozszerzenie Tabeli nie uniemożliwi tworzenia podklas wykresów, gdzie T nie rozszerza ChartComponent. – IAmYourFaja

Odpowiedz

4

Twoja klasa Chart zawiera List<T> że mówisz, więc jeśli można zdefiniować swoją CartesianChart klasy abstrakcyjnej przedłużyć Chart<CartesianComponent>, mówisz, że List<T> jest naprawdę List<CartesianComponent>.

Naprawdę, wystarczy użyć standardu, jaki zdefiniowałeś w klasie abstrakcyjnej (czyli <T extends CartesianComponent>). Próbowałbym to zrobić i zobaczyć, jak to działa.

public abstract class CartesianChart<T extends CartesianComponent> extends Chart<T> 
{ 
    // ... 
} 
+0

To jest bardzo dobra sugestia (i dziękuję!), Jednak obawiam się, że pozwoliłoby to na zdefiniowanie podklas: schemat klasy publicznej rozciąga wykres , gdzie Widget nie jest podklasą ChartComponent. Ważne jest, aby wszystkie typy "T" dziedziczyły (w pewnym momencie) z ChartComponent. – IAmYourFaja

+0

Nie byłoby to w rzeczywistości, ponieważ już zdefiniowałeś 'T' w tym zakresie, aby był ograniczony do' CartesianComponent's (na 'KartezjańskiChart '). Wypróbuj moją sugestię, a następnie spróbuj dodać 'Widget' do niej ... –

+0

Awesome, awesome, awesome. Jeszcze raz dziękuję! – IAmYourFaja

0

Użyj interfejsów.

public interface IsAPieChart { 

} 

public interface IsACartesianChart { 

} 

Nie potrzebują nawet żadnych metod.

Twój profil metoda addLine() będzie czytać:

public void addLine(IsACartesianChart cartesianChart); 

Twoje klasy abstrakcyjne byłoby przeczytać:

public class PieChart extends Chart<PieWedge> implements IsAPieChart 
{ 
    // ... thus its list of ChartComponents is actually a List<PieWedge> 
} 

I używać IsACartesianChart oznaczyć CartesianChart w ten sam sposób. Teraz addLine() nie zaakceptuje niczego z PieChart, ponieważ żaden PieChart nie implementuje interfejsu IsACartesianChart, ale zajmie to wszystko z podklasy CartesianChart, ponieważ wszystkie podklasy implementują IsACartesianChart.

Korzystanie interfejsy jak jest to świetny sposób, aby przywrócić wyróżnień, które zostały utracone, gdy kilka klas prześledzić wstecz do tej samej klasy bazowej. Nadklasy i podklasy tworzą ścisłą hierarchię, podczas gdy interfejsy można dołączać tam, gdzie ich potrzebujesz.

0

Powodem mamy T extends ChartComponent dlatego każdy wykres podklasa będzie zawierał 1+ tzw elementów wykresu (bary, linie, klinów, etc.):

To jest twój czerwony-śledź, tutaj nie ma potrzeby używania generycznego. Jest to problem o numerze Composition, a nie problem z Generics.

Tylko lista wygląda tak:

private List<ChartComponent> components; 

To wszystko bezpieczeństwo typu powinny trzeba.

+0

Dzięki! Spróbuję tej sugestii później w tym tygodniu, kiedy będę miała szansę. – IAmYourFaja