【问题标题】:Generify UI components生成 UI 组件
【发布时间】:2011-01-12 22:38:48
【问题描述】:

我尝试了几天来确定是否可能,我失败了,但也许这是可能的(我认为应该是可能的)。

假设我们有一些类似于 Swing 层次结构的 UI 组件 + 我们将使用流畅的界面 Fluent Interfaces

public abstract class Component {

     ...

     public abstract Component setName(String name);

     public abstract String getName();

     ...

}

public abstract class Panel extends Component {
     ....
}

public abstract class TitledPanel extends Panel {
     ....

     public abstract TitledPanel setTitle(String title);

     public abstract String getTitle();
} 

有没有可能,使用泛型能够写出这样的东西?

new TitledPanel().setName("panel").setTitle("Title);

setName 应该返回 TitledPanel 而不是 Component 以便能够链接这些调用。

这只是一个简单的例子,但想法是一旦我有一个类型为 T 的对象,任何对超类的任何流利方法的调用都返回类型 T。

编辑 1:我忘了排除重写方法并返回协变类型的部分 :) 如果确实可能的话,我只想要普通的泛型。

【问题讨论】:

    标签: java generics user-interface components fluent-interface


    【解决方案1】:

    在您的 TiltedPanel 中(顺便说一句,它不能是抽象的,如果您想新建一个),您可以覆盖 Component 中的抽象方法并更改返回类型。返回类型不是方法签名的一部分,因此您可以使用不同的返回类型来实现它:

    public class Test
    {
        public static void main(String[] args)
        {
            TiltedPanel p = new TiltedPanel().setName("panel").setTitle("title");
            System.out.println("name = " + p.getName());
            System.out.println("title = " + p.getTitle());
        }
    }
    
    abstract class Component
    {
        public abstract Component setName(String name);
    
        public abstract String getName();
    }
    
    abstract class Panel extends Component
    {
    }
    
    class TiltedPanel extends Panel
    {
        private String title;
        private String name;
    
        public TiltedPanel setName(String name)
        {
            this.name = name;
            return this;
        }
    
        public String getName()
        {
            return this.name;
        }
    
        public TiltedPanel setTitle(String title)
        {
            this.title = title;
            return this;
        }
    
        public String getTitle()
        {
            return this.title;
        }
    }
    

    【讨论】:

    • 确实有可能,但我希望它没有协变类型:)
    【解决方案2】:

    首先我建议只使用 set 方法,然后删除 set。

    您可以使用协变返回类型,但这意味着覆盖每个派生类中的每个方法。但是,它确实涉及很多非常繁琐的代码。

    public abstract class Component {
        ...
        public Component name(String name) {
            this.name = name;
            return this
        }
    }
    
    public abstract class Panel extends Component {
        ...
        public Panel name(String name) {
            super.name(name);
            return this;
        }
    }
    

    添加一个通用的THIS 参数,作为枚举,使实现更容易,但客户端代码可能需要在声明中添加一个<?>

    public abstract class Component<THIS extends Component<THIS>> {
        ...
        protected abstract THIS getThis();
        ...
        public THIS name(String name) {
            this.name = name;
            return this
        }
    }
    
    public abstract class Panel<THIS extends Panel<THIS>> extends Component<THIS> {
        ...
    }
    
    public class TitledPanel extends Panel<TitledPanel> {
        ...
        public TitledPanel getThis() {
            return this;
        }
    }
    

    另一种方法是使用双括号成语。

    new TitledPane() {{
        name("panel");
        title("Title");
    }}
    

    【讨论】:

    • "首先我建议只使用 set 方法,然后删除 set。" - 没看懂这部分。
    • 哪一部分?我建议不要使用 get 方法——它们没用。我还建议不要使用 set 前缀,因为它丑陋且不必要。
    • 您需要有 get 方法才能访问这些成员。好吧,set 和 get 是有意义的,这也是 Sun 的命名约定:java.sun.com/docs/codeconv/html/CodeConventions.doc8.html 并不是说​​ Sun 没有任何违反规则,但这些简单的规则是有意义的,至少对我来说是这样。
    • 应该从实例内部访问这些成员。 set 没有get 只是噪音。
    • 将 Component 视为 java.awt.Component,必须有公共方法来访问组件成员:边界、名称、布局等。无论如何,没关系 :) 这可以利用吗泛型,这是我的问题?如果不是,我会说他们做错了什么:) setName 应该是相同的类型(TitledPanel)而不是超类型(Component),因为调用是在 TitledPanel 上进行的。
    【解决方案3】:

    我不确定您是否可以使用泛型来实现这一点。你可以做的是这样的:

    public abstract class Component {
    
    private String name;
    
    public Component setName(String name) {
        this.name = name;
        return this;
    }
    
    public String getName() {
        return name;
    }
    

    }

    public abstract class Panel extends Component {
    

    }

    public class TitledPanel extends Panel {
    
    private String title;
    
    public TitledPanel setTitle(String title) {
        this.title = title;
        return this;
    }
    
    @Override
    public TitledPanel setName(String name) {
        super.setName(name);
        return this;
    }
    
    public String getTitle() {
        return title;
    }
    

    }

    现在new TitledPanel().setName("panel").setTitle("Title"); 可以工作了

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-02-20
      • 2012-10-02
      • 1970-01-01
      • 2011-07-29
      • 2010-12-29
      • 1970-01-01
      • 1970-01-01
      • 2014-11-10
      相关资源
      最近更新 更多