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

Use derived type for member variable in derived class

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

class是这样的:

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 中可用,因此我需要通过提供方法 "workaround" 在 B 中执行 "workaround" =16=].

是的,通常我们在 A 中也会有一个 getButton() 方法,所以这不是对 B 的牵强附会。

但我想知道,这真的是正确的做法还是我做错了?

我也想过创建一个摘要 class,例如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() 或类似的东西,但它会变成一个全新的错误做法。

一种简单(和多态)的方法是覆盖 class B 中的 getButton 和 return IconButton 的实例,而不是在 class,例如:

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;
    }
}

您的解决方法是个坏主意。你失去了很多类型安全。你可以例如将 B 的构造函数更改为 x = new Button(); 并忘记强制转换。您的程序仍然可以编译但在运行时失败。

我认为您在通用泛型超类方面走在正确的轨道上。您可以将 A0 定义为带有成员 T xA0<T extends Button>。可能有更好的解决方案,但没有大局就很难说。

  1. 提取按钮界面
    public interface Button {
        void init();
    }
    
  2. 创建 PlainButton(您的前 Button)class:
    public class PlainButton implements Button {
        public void init() {
            xx();
        }
        private void xx() {
        }
    }
    
  3. 创建 IconButton class:
    public class IconButton implements Button {
        public void init() {
            yy();
            zz();
        }
        private void yy() {
        }
        private void zz() {
        }        
    }
    
  4. 创建 ButtonWrapper class(你的前 A and/or B classes):
    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 中为它创建一个单独的成员,并让 A 使用它?

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
}

不需要抽象基础class、泛型、包装器。让 B 拥有它的 IconButton,并有 2 个引用(B.ib 和继承 A.b

如果您不想使用泛型: 这种方法违背了 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)时最好使用惰性初始化而不是构造初始化:

public static class A {

        private Button x = null;

        protected Button getButton() {
            if (x == null) x = createButton();
            return x;
        }

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

    public static class B extends A{

        private IconButton iconButton; 

        public Button getButton() {
            if (iconButton == null) iconButton = createButton();
            return iconButton;
        }

        public IconButton getIconButton(){
            getButton();
            return iconButton;
        }

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