2016-08-18 22 views
9

Załóżmy, że potrzebuję jakiegoś DerivedBuilder do rozszerzenia niektórych BaseBuilder. Podstawowy program budujący ma pewną metodę, taką jak foo (która zwraca BaseBuilder). Wyprowadzony konstruktor ma metodę bar. Metoda bar powinna zostać wywołana po metodzie foo. W tym celu należy go mogę zastąpić foo metodę w DerivedBuilder tak:Generic Java Generator

@Override 
public DerivedBuilder foo() { 
    super.foo(); 
    return this; 
} 

Problemem jest to, że BaseBuilder ma wiele sposobów, takich jak foo i muszę zastąpić każdą z nich. Nie chcę, aby to zrobić, więc starałem się używać rodzajowych:

public class BaseBuilder<T extends BaseBuilder> { 
    ... 

    public T foo() { 
     ... 
     return (T)this; 
    } 
} 

public class DerivedBuilder<T extends DerivedBuilder> extends BaseBuilder<T> { 
    public T bar() { 
     ... 
     return (T)this; 
    } 
} 

Ale problemem jest to, że nadal nie można napisać

new DerivedBuilder<DerivedBuilder>() 
     .foo() 
     .bar() 

Nawet T tutaj jest DerivedBuilder. Co mogę zrobić, aby nie zastąpić wielu funkcji?

+1

Z tego approch generyków, można zadzwonić: 'nowy DerivedBuilder (). foo(). bar() '. To zadziała i uruchomi najpierw 'foo', a potem' bar'. Jeśli chcesz wywołać więcej metod 'BaseBuilder' i wreszcie chcesz wywołać metodę' DerivedBuilder', to nie jest to możliwe, ponieważ po raz drugi metoda zwraca referencję 'BaseBuilder', z tym nie możesz nazwać' DerivedBuilder'' s metoda. –

+0

@ Sandeep.KI próbował tego dokonać, a kompilator narzekał, że 'bar' nie został zdefiniowany w' BaseBuilder' ponieważ wtedy robię '.foo(). Bar()' jedyny kompilator wie o 'T' po wykonaniu' foo 'jest to, że to' T' rozszerza 'BaseBuilder' – PepeHands

+0

Zobacz [this] (http://stackoverflow.com/questions/4217013/how-to-force-derived-class-to-call-super-method-like-android -does) i [this] (http://stackoverflow.com/questions/6853638/require-override-of-method-to-call-super) i [this] (http://stackoverflow.com/questions/6142460/how-do-i-force-a-polimorficzne wywołanie-super-metody) ... – user1803551

Odpowiedz

5

Twój problem jest definicja DerivedBuilder:

class DerivedBuilder<T extends DerivedBuilder>; 

A potem uruchamianiu go typu usunięte argumentu new DerivedBuilder<DerivedBuilder<...what?...>>().

Musisz w pełni zdefiniowany typ pochodzący coś takiego:

public class BaseBuilder<T extends BaseBuilder<T>> { 
    @SuppressWarnings("unchecked") 
    public T foo() { 
     return (T)this; 
    } 
} 

public class DerivedBuilder extends BaseBuilder<DerivedBuilder> { 
    public DerivedBuilder bar() { 
     return this; 
    } 
} 

Sprawdź ideone.com.

+0

Czy istnieje również możliwość dalszego wyprowadzenia 'DerivedBuilder', na przykład' SubDerivedBuilder'? 'SubDerivedBuilderM <...> extends DerivedBuilder <...>' –

+1

@MCEmperor tak, ale musisz sprawić, aby wszystkie najbardziej wyprowadzone typy przekazywały pełne typy do swojej klasy bazowej. Sprawdź [to widelec] (http://ideone.com/ZIAuqs) – BeyelerStudios

0

Z twojego pytania wiem, że metoda foo() powinna zostać wykonana przed paskiem metody().
Jeśli jest to prawidłowe, można zastosować wzór szablonu.
Utwórz abstrakcyjny pasek metod w BaseBuilder.
A nowa metoda mówi "szablon". Szablon metody definiuje sekwencję - najpierw wykonywane jest polecenie foo(), a następnie bar().
DerivedBuilder zapewni implementację paska metod.

public abstract class BaseBuilder { 

    public void foo(){ 
     System.out.println("foo"); 
    } 

    public abstract void bar(); 

    public void template(){ 
     foo(); 
     bar(); 
    } 
} 

public class DerivedBuilder extends BaseBuilder{ 

    @Override 
    public void bar() { 
     System.out.println("bar");  
    } 

    public static void main(String[] args) { 
     BaseBuilder builder = new DerivedBuilder(); 
     builder.template(); 
    } 
} 


Nadzieja to pomaga.

2

Zamiast odlewania return (T) this; Tutaj zrobiłem Class.cast(this).

Aby zrealizować:

BaseBuilder.build(ExtendedBuilder.class).foo().bar().foo().bar(); 

Każda klasa w hierarcha musi znać rzeczywistą końcową klasę dzieci, dlatego zdecydowałem się dokonać metoda fabrykabuild w klasie bazowej.

Obsada this do rzeczywistego dziecka jest również wykonywana w ostatecznej metodzie klasy bazowej, podając return me();.

class BaseBuilder<B extends BaseBuilder<B>> { 

    protected Class<B> type; 

    public static <T extends BaseBuilder<T>> T build(Class<T> type) { 
     try { 
      T b = type.newInstance(); 
      b.type = type; 
      return b; 
     } catch (InstantiationException | IllegalAccessException e) { 
      throw new IllegalStateException(e); 
     } 
    } 

    protected final B me() { 
     return type.cast(this); 
    } 

    B foo() { 
     System.out.println("foo"); 
     return me(); 
    } 
} 

class ExtendedBuilder extends BaseBuilder<ExtendedBuilder> { 

    ExtendedBuilder bar() { 
     System.out.println("bar"); 
     return me(); 
    } 
} 
+0

A teraz masz fabrykę budowniczych. Ponadto, pole 'type' i' typ.cast (this) 'jest bezcelowe, ponieważ nie zapewnia żadnego dodatkowego bezpieczeństwa podczas kompilacji i komplikuje całą sprawę. – Clashsoft

+0

@XetraSu zaproponował 'klasę statyczną', która prawdopodobnie pasuje do ich użycia. –

4

Oprócz BeyelerStudios's answer, jeśli chcesz zagnieździć dalej, można po prostu użyć tego:

class Base<T extends Base<?>> { 
    public T alpha() { return (T) this; } 
    public T bravo() { return (T) this; } 
    public T foxtrot() { return (T) this; } 
} 

class Derived<T extends Derived<?>> extends Base<T> { 
    public T charlie() { return (T) this; } 
    public T golf() { return (T) this; } 
} 

class FurtherDerived<T extends FurtherDerived<?>> extends Derived<T> { 
    public T delta() { return (T) this; } 
    public T hotel() { return (T) this; } 
} 

class MuchFurtherDerived<T extends MuchFurtherDerived<?>> extends FurtherDerived<T> { 
    public T echo() { return (T) this; } 
} 

public static void main(String[] args) { 
    new MuchFurtherDerived<MuchFurtherDerived<?>>() 
     .alpha().bravo().charlie().delta().echo().foxtrot().golf().hotel() 
     .bravo().golf().delta().delta().delta().hotel().alpha().echo() 
     .echo().alpha().hotel().foxtrot(); 
}