在派生类中为成员变量使用派生类型

问题描述:

我目前有一个类A,它有一些我想在B中使用的功能.A有一个类型为Button的成员变量,而在BI中想要使用类型IconButton(从按钮派生)。在派生类中为成员变量使用派生类型

的类是这样的:

public class A { 

    protected Button x = new Button(); 

    A() { 
     x.xx(); 
    } 

    public void doFirstThing() { 
     x.xx(); 
    } 

    Button getButton() { 
     return x; 
    } 
} 

public class B extends A { 

    B() { 
     x = new IconButton(); 

     getButton().yy(); 
    } 

    public void doSecondThing() { 
     getButton().zz(); 
    } 

    IconButton getButton() { 
     return (IconButton) x; 
    } 
} 

public class Button { 

    public void xx() { 

    } 
} 

public class IconButton extends Button { 

    public void yy() { 

    } 

    public void zz() { 

    } 
} 

正如你所看到的,方法yy()zz()仅在IconButton可用,因此,我需要通过提供一种方法getButton做B中的“解决方法”。

是的,通常我们不得不以一种过于一个getButton()方法,所以这不是一个牵强除了B.

但我不知道,这是真的做的正确的方式或我做错了?

我也想过创建一个抽象类,例如A0,将包含来自A.当前代码然后,我只是做:

class A extends A0<Button> { 
// mostly empty, code taken from A0 
} 

class B extends A0<IconButton> { 
// custom things here 
} 

还是有其他的办法是对这个问题真的更好吗?

这是我的GWT应用程序中的组件。所以,如果我声明像@king_nak说的那样,那么我的组件中会有两个按钮。或者如果我可以接受另一种解决方法,我可以删除B中的原始按钮,例如x.removeFromParent()或类似的东西,但它会成为一个全新的不良习惯。

+0

怎么样'抽象类A '有场'T x' ,而'B类延伸A '? –

+0

我不确定你是否打算在某个时候构造一个'A'类的实例。如果是这样,那么它可以,除了'IconButton getButton()'不是必需的。如果您不会创建'A'的实例,那么将其抽象为是 – KarelG

+0

IconButton是B的实现细节。为什么要将IconeButton和Button暴露给这些类的客户端? – davidxxx

你的解决方法是一个坏的理念。你松了很多类型的安全。你可以例如把B的建造者改为x = new Button();,忘掉演员。您的程序仍然可以编译,但运行时会失败。

我认为你和普通的通用超类是正确的。您可以将A0定义为A0<T extends Button>与成员T x。可能有更好的解决方案,但如果没有大局,很难说清楚。

+0

我已经更新了我的问题,关于我的用例的更多细节。它是一个GWT应用程序(A和B是真正的组件),并且需要在A和B中的许多地方使用yy()和zz() – ramcrys

+0

@ramcrys不,我不是GWT专家。为了在全局范围内彻底地回答这个问题,这将变成广泛的计算器。 –

+0

接受了答案:)我已经去了在我的代码中使用泛型方法。将不得不观察它将来如何运作。 – ramcrys

一个简单的(和多态)的方式将覆盖B级getButton并返回IconButton一个实例,而不是具有所有的类的引用,例如:

public class A { 
    public Button getButton(){ 
     Button b = new Button(); 
     b.xx(); 
     return b; 
    } 
} 

public class B extends A { 

    @Override 
    public Button getButton(){ 
     IconButton b = new IconButton(); 
     b.yy(); 
     b.zz(); 
     return b; 
    } 
} 
+0

在这种情况下,有一些工厂设计模式,如果你问我,会导致更简洁的代码。 – KarelG

+0

如果'getButton'方法返回的实例类型取决于覆盖它的类,那么我宁愿用这种方式去实现工厂方法。 –

+0

为什么要这样做,如果类型(这取决于类)从'Button'完全继承?干净的方法是一个界面,但我不确定在这种情况下,因为我没有看到OP的缩进cleary。在设计软件解决方案时,这种讨论非常平常。 – KarelG

  1. 提取按键接口
     
    public interface Button { 
        void init(); 
    } 
    
  2. 创建PlainButton(你的前扣)类:
     
    public class PlainButton implements Button { 
        public void init() { 
         xx(); 
        } 
        private void xx() { 
        } 
    } 
    
  3. 创建IconButton类:
     
    public class IconButton implements Button { 
        public void init() { 
         yy(); 
         zz(); 
        } 
        private void yy() { 
        } 
        private void zz() { 
        }   
    } 
    
  4. 创建ButtonWrapper类(你的前一个和/或B类):
     
    public class ButtonWrapper { 
        private final T button; 
        public ButtonWrapper(T button) { 
         this.button = button; 
         this.button.init(); 
        } 
        public T getButton() { 
         return button; 
        } 
    } 
    
  5. 用途:

     
    ButtonWrapper A = new ButtonWrapper(new PlainButton()); 
    ButtonWrapper B = new ButtonWrapper(new IconButton()); 
    

如果你唯一担心的是,B应该有IconButton,而不是它的基地Button,为什么不B中创建一个单独的成员吧,让一个用呢?

class A { 
    protected Button b; 
    public A() { 
     // By default, I use a Button 
     b = new Button(); 
    } 

    // Subclasses can override which button I use 
    protected A(Button b) { 
     this.b = b; 
    } 

} 

class B extends A { 
    private IconButton ib; 
    public B() { 
     super(new IconButton()); 
     ib = (IconButton) super.b; 
    } 

    // B can access it's IconButton through ib 
    // Users of A will see a Button 
} 

不需要抽象基类,泛型,包装器。只是让B具有它的IconButton,并有2名对它的引用(B.ib和继承A.b

+0

Ahhhhh ...我忘了提及,这是我的GWT应用程序中的一种组件。所以,如果我像你说的那样声明,那么我的组件中会有两个按钮...(或者如果我可以与另一个解决方法一起生活,我可以删除B中的原始按钮,但它只是成为一个全新的级别不好的练习) – ramcrys

+0

实例化B时只有1个按钮。当子类提供一个时,永远不会创建按钮。或者你正在处理生成的代码,不能使用这种方法? –

+0

Woah ..好像这就是我需要的东西。但是当我们有两件事情(b和ib)在概念上应该只是一个时,它看起来很奇怪/在这里介绍一个坏习惯? 我的意思是,你的方法可能是最好的实际工作之一,但它真的是一个干净的设计? – ramcrys

在你不情况下要使用泛型: 这种做法违背best practices,但完全罚款,只要你不这样做,我觉得它任何在B的构造与按钮:

public static class A { 

    protected Button x = null; 

    public A() { 
     x = createButton(); 
    } 

    public Button getButton() { 
     return x; 
    } 

    protected Button createButton(){ 
     Button result = new Button(); 
     result.xx(); 
     return result; 
    } 
} 

public static class B extends A{ 

    public IconButton getButton() { 
     return (IconButton)x; 
    } 

    protected Button createButton(){ 
     IconButton iconButton = new IconButton(); 
     iconButton.yy(); 
     iconButton.zz(); 
     return iconButton; 
    } 
} 

现在,如果你想避免铸造你可以使用我很有可能不会用自己这个疯狂的做法。 警告使用您自己的风险:

public static class A { 

     protected Button x = null; 

     public A() { 
      x = createButton(); 
     } 

     public Button getButton() { 
      return x; 
     } 

     protected Button createButton(){ 
      Button result = new Button(); 
      result.xx(); 
      return result; 
     } 
    } 

    public static class B extends A{ 

     //leave uninitialized so that when B gets constructed iconButton wont be reinitialized because it has been initialized by A's constructor call 
     private IconButton iconButton; 

     public IconButton getButton() { 
      return iconButton; 
     } 

     protected Button createButton(){ 
      iconButton = new IconButton(); 
      iconButton.yy(); 
      iconButton.zz(); 
      return iconButton; 
     } 
    } 

更新:

随着讨论的首选方法是避免在继承和这里如果需要用的组合物。但由于示例代码没有太多细节以便能够显示组合方法,所以我添加了仍然使用继承的另一种方法,避免了强制转换和构造初始化。 我认为这是最好的像Swing或GWT,而不是建设初始化非并发框架工作时使用延迟初始化:

​​
+0

其实,我不只是在B中的构造函数中使用x。换句话说,在B中可能有很多其他方法,我想调用某些东西特殊(仅来自IconButton)。所以我们只需要采取我原来的解决方法:在所有这些地方调用getButton():( – ramcrys

+0

您需要在'createButton()'中保留所有'iconButton'初始化代码。 – tsolakp

+0

yes对于初始化但是如果我有一个需要调用x.yy()的方法B.handlerForEventXXX()会怎样呢?这是不可能的=>我必须调用getButton()。yy().. (我正在谈论你的第一种方法),请看看我在这个问题中更新的代码 – ramcrys