2015-05-05 26 views
4

比方說,我有一個類,它實現了一個方法(addThings())。它作爲子類的樹的基礎:如何強制我的類的任何子類始終調用它們覆蓋的父級實現方法?

ParentClass { 
    protected void addThings() { 
     map.add(thing1); 
     map.add(thing2); 
    } 
} 

現在,讓我們說,我們實現一個子類(其中有事3爲好)和東西3還需要在事情1的頂部添加事情2

顯而易見的Java解決方案似乎是有孩子的類的實現方法的調用超類的方法:

ChildClass extends ParentClass { 
    protected void addThings() { 
     super.addThings(); 
     map.add(thing3); 
    } 
} 

的問題是,誰實現了子類很可能忘了做,有一個錯誤

ChildClassBad extends ParentClass { 
    protected void addThings() { 
     // BUG!!! Forgot to call super.addThings(); !!! 
     map.add(thing3); 
    } 
} 

有沒有在Java中的方式來任何延長孩子(以及孫子)的類始終調用AP arent的方法,如果他們重寫它? (類似於抽象方法總是迫使他們實現它)。

  • 請注意,我需要解決方案沿着繼承樹進行傳播。

    換句話說,如果有人實現了需要添加Thing4的GrandChildClass,他們將遭受相對於ChildClass相同的錯誤可能性。

    這意味着在ParentClass中分別具有單獨的「addParentThings()」,然後同時調用addParentThings()和可覆蓋兒童的addThings()的簡單修復(適用於只有1級繼承)的情況下,不足(因爲孫子必須覆蓋非空的addThings)。

+1

據我所知,你不能加強對超級方法的調用。你能給我們更多的細節,爲什麼你要使用這樣的機制?也許你有[XY-Problem](http://meta.stackexchange.com/questions/66377/what-is-the-xy-problem)? – Turing85

+0

@ Turing85 - 我正在實施一個會在那裏待上一段時間的父類,很可能會被分類很多。我發現了一個明顯的**可能的錯誤,任何子類實現者都將受到影響,我想要一個基於Java編譯器的解決方案來解決這個問題(而不是「寫一條評論告訴人們這樣做,並希望他們閱讀評論「) – DVK

+0

我得到你的一般想法,但我認爲你可以用另一種方式來防止這個問題。在給定的例子中,你可以通過構造函數強化你的條件,但我認爲你的情況並不那麼簡單。 – Turing85

回答

2

如果你願意讓你的doStuff - 方法靜態爲每個類,它擴展了您的ParentClass,給你一個ParentClassfinal public void doAllStuff() - 方法,可以解決反射問題:

import java.lang.reflect.Method; 

import java.util.ArrayList; 
import java.util.List; 

public class Main 
{ 
    public static void main(String[] args) throws InterruptedException 
    { 
     A a = new C(); 
     a.doAllStuff(); 
    } 
} 

class A 
{ 
    protected List<String> list = new ArrayList<String>(); 

    @SuppressWarnings("unused") 
    private static void doStuff(A a) 
    { 
     a.list.add("I am A"); 
    } 

    final public void doAllStuff() 
    { 
     List<Class<?>> list = new ArrayList<Class<?>>(); 
     Class<?> clazz = this.getClass(); 
     while (A.class.getSuperclass() != clazz) 
     { 
      list.add(clazz); 
      clazz = clazz.getSuperclass(); 
     } 
     System.out.println(list); 
     for (Class<?> myClass : list) 
     { 
      try 
      { 
       Method method = myClass.getDeclaredMethod("doStuff" 
                  , myClass); 
       // Method is private? Make it accessible anyway. 
       method.setAccessible(true); 
       method.invoke(this, this); 
      } 
      catch (NoSuchMethodException e) 
      { 
       // Method not found, continue with next class. 
       continue; 
      } 
      catch (Exception e) 
      { 
       e.printStackTrace(); 
      } 
     } 
     System.out.println(this.list); 
    } 
} 

class B extends A 
{ 
    @SuppressWarnings("unused") 
    private static void doStuff(B b) 
    { 
     b.list.add("I am B"); 
    } 
} 

class C extends B {} 

如果您需要只能調用屬性,您可以使用getDeclaredField,在這種情況下字段可能不是static

+0

到目前爲止,這是所有答案中最好的方法。到目前爲止,唯一的小缺點是(1)它是靜態限制的(坦率地說,我不明白爲什麼它不能很容易地擴展到對象方法?你可以通過使用下層調用者對象的類'的getClass()'); (2)它打開了一個反向錯誤,B類的實現者在他的實現中意外地將一個調用添加到了super.doStuff()中。我認爲這個也可以解決。 – DVK

+0

由於缺點,我推遲接受答案,但很可能會接受這個答案,除非有更好的建議。 – DVK

+0

@DVK對象方法的問題是動態綁定:調用'B'類型對象的'doStuff'必須總是導致對'B.doStuff()'的調用(這就是動態綁定的性質)。我通過將「doStuff」方法設置爲「私人」來「取消」「意外」調用。這樣,它們只在課堂上可見,不能從外部訪問。另一種可能的方法是[裝飾模式](http://en.wikipedia.org/wiki/Decorator_pattern),但爲此,您必須將每個對象包裝在一個新對象中。 – Turing85

5

也許嘗試有一個最後的方法,調用另一個可重寫的方法?

class ParentClass { 

    public final void doStuff() { 
     // Do stuff 
     onPostDoStuff(); 
    } 

    protected void onPostDoStuff() { 
     // Override this! 
    } 
} 

然後在子類:

class ChildClass extends ParentClass { 

    @Override 
    protected void onPostDoStuff() { 
     // Do extra stuff 
    } 
} 

你甚至可以使onPostDoStuff()方法抽象的,所以孩子來覆蓋它。

+3

這似乎**並非所有**幫助孫子壓倒一切的孩子班的方法? (你似乎只是在我的帖子的後半部分明確拒絕了我的解決方案,並且對它進行了一次「最終」處理,儘管沒有任何幫助) – DVK

+1

我認爲這仍然是最好的方法。如果您需要進一步將其應用於大型子類,請使用'onPostDoStuff()'final'並再次應用相同的原則。 –

+1

@DidierL - 如果我需要一個grandgrandchild,使另一種方法?作爲一種方法,這似乎令人難以置信。此外,**並沒有回答這個問題,因爲這個問題非常明確地拒絕了這種方法,因爲不適當的 – DVK

0

我想不出任何能夠滿足你在未來的子類中仍然可執行的條件。

Android sdk在他們的許多核心生命週期方法中都有「超級未被調用」異常,但它嚴格地是單級繼承。

1

以下方法強制調用超類的設置方法。子類中的缺點或可能的錯誤是,實現者可能會忘記提供合適的構造函數來爲安裝方法提供擴展。這意味着孩子不能被孫子延長。

它也很難看,因爲子類不能在其擴展中插入子類特定的設置;他們必須接受Parent作爲參數。

總的來說,這裏的尷尬困難表明,如果真的在靜態分析器中執行,而不是javac,則執行效果會更好。

public class Parent 
{ 

    private final Consumer<Parent> setup; 

    protected final Collection<Object> x = new ArrayList<>(); 

    public Parent() 
    { 
    setup = Parent::setupImpl; 
    } 

    protected Parent(Consumer<Parent> extension) 
    { 
    setup = ((Consumer<Parent>) Parent::setupImpl).andThen(extension); 
    } 

    public final void setup() 
    { 
    setup.accept(this); 
    } 

    private static void setupImpl(Parent it) 
    { 
    it.x.add("thing1"); 
    it.x.add("thing2"); 
    } 

} 

public class Child 
    extends Parent 
{ 

    public Child() 
    { 
    super(Child::setupImpl); 
    } 

    protected Child(Consumer<Parent> extension) 
    { 
    super(((Consumer<Parent>) Child::setupImpl).andThen(extension)); 
    } 

    private static void setupImpl(Parent it) 
    { 
    it.x.add("thing3"); 
    } 

} 
1

這是解決你的問題稍微不同的方法,如註釋到選定的回答說,你可以使用Decorator模式之一(它是從傳統的裝飾有點不同,適應這一問題),我認爲這是一個更清潔的解決方案。我添加了2個添加事物3和事物4的類來顯示用法。

public interface ThingAdder { 
    void addThings(); 
} 

public abstract class AbstractAdder implements ThingAdder { 
    protected List<String> map = new ArrayList<>(); // or your map impl 
} 

public class DefaultAdderDecorator implements ThingAdder { 
    AbstractAdder decoratedThingAdder; 

    public DefaultAdderDecorator(AbstractAdder decoratedThingAdder) { 
    this.decoratedThingAdder = decoratedThingAdder; 
    } 

    @Override 
    public void addThings() { 
     decoratedThingAdder.map.add("thing 1"); 
     decoratedThingAdder.map.add("thing 2"); 
     decoratedThingAdder.addThings(); 
    } 
} 

public class Thing3Adder extends AbstractAdder { 
    @Override 
    public void addThings() { 
    map.add("thing 3"); 
    } 
} 

public class Thing4Adder extends AbstractAdder { 
    @Override 
    public void addThings() { 
    map.add("thing 4"); 
    } 
} 

public class AdderApp { 
    public static void main(String args[]) { 
    Thing3Adder thing3Adder = new Thing3Adder(); 
    Thing4Adder thing4Adder = new Thing4Adder(); 
    ThingAdder decoratedAdder = new DefaultAdderDecorator(thing3Adder); 

    decoratedAdder.addThings(); 
    System.out.println("Decorated Thing3Adder map:"+thing3Adder.map); 

    decoratedAdder = new DefaultAdderDecorator(thing4Adder); 

    decoratedAdder.addThings(); 
    System.out.println("Decorated Thing4Adder map:"+thing4Adder.map); 
    } 
} 

運行AdderApp後,這是印刷:

Decorated Thing3Adder map:[thing 1, thing 2, thing 3] 
Decorated Thing4Adder map:[thing 1, thing 2, thing 4] 

Decorator模式背後的理念是通過使用默認的裝飾,增加了東西來增強現有功能,在這種情況下,我們擴充了addThings方法1和thing 2在調用裝飾對象自己的addThings方法之前,那麼無論何時需要有一個需要首先插入默認值的新加法器,開發人員纔會創建一個新的ThingXAdder來擴展AbstractAdder。

相關問題