【问题标题】:Does Java bean's setter permit return this?Java bean 的 setter permit 是否返回这个?
【发布时间】:2011-04-21 08:17:31
【问题描述】:

我可以定义 setter 方法来返回 this 而不是 void 吗?

喜欢:

ClassA setItem1() {
      return this;
}

ClassA setItem2() {
      return this;
}

然后我可以使用 new ClassA().setItem1().setItem2()

【问题讨论】:

标签: java design-patterns


【解决方案1】:

对 JavaBeans 规范有很多误解。

它存在的主要原因是统一的Java“组件”模型。这是一种使用反射以编程方式与 Java 对象交互的方法。 API 本身被命名为JavaBeans Introspection。请看一下示例用法,您会比一般的 Java 程序员知道的更多。

Introspection API 可用于统一操作 GUI 元素。您的组件将其属性公开为一对 getter 和 setter,以便可以在运行时在 GUI 构建器的属性表上发现和操作它们。

因此,在我看来,混合使用 fluent API 和 JavaBeans Spec 是不行的。这是两个完全不相关的概念,可以相互干扰。当方法签名不同(返回类型)时,JavaBeans Introspection 可能不起作用。

看看这个例子(取自链接教程):

public class SimpleBean
{
private final String name = "SimpleBean";
private int size;

public String getName()
{
    return this.name;
}

public int getSize()
{
    return this.size;
}

public void setSize( int size )
{
    this.size = size;
}

public static void main( String[] args )
        throws IntrospectionException
{
    BeanInfo info = Introspector.getBeanInfo( SimpleBean.class );
    for ( PropertyDescriptor pd : info.getPropertyDescriptors() )
        System.out.println( pd.getName() );
}
}

此示例创建一个非可视 bean 并显示从 BeanInfo 对象派生的以下属性:

  • 姓名
  • 尺寸

您可能想看看当您将void 返回类型更改为其他任何内容时会发生什么。我已经这样做了,结果是一样的。那么,这是否意味着它是允许的?

恐怕没有。 JavaBeans 规范对这些方法签名非常严格。只是碰巧实施是宽容的。尽管如此,我不建议将流式接口与 JavaBeans 混合使用。你不能真的相信这一点,如果发现现在有效,那么将来也会有效。

但是,从另一方面来看 - 看起来您并没有完全使用 JavaBeans。只有 getters/setters 对方法。如何实现和设计 API 取决于您。

【讨论】:

  • 很好的示例,我很惊讶这实际上适用于非空设置器。需要注意的一件事是,并非所有框架都使用 Introspector 而是实现自己的逻辑。因此,在使用 EL、jpa 或 jaxb 时,您可能会得到不同的结果。
  • 您能否引用 JavaBeans 规范中要求属性设置器方法返回 void 类型的任何部分?我不是在寻找例子。我正在寻找说明该规范的实际要求。第 8.2 节表明它不是要求。
  • 看一下反射api。在反射方法的检索和调用中,没有任何地方考虑过返回类型。 Method.invoke 返回 Object,它从不查看返回类型。具有相同名称和参数签名的两个方法是不合法的,因此,返回类型不会影响反射方法调用。
  • 从 Java 7 开始,正在检查返回类型:stackoverflow.com/q/10806895/309259
  • @Rekin 您链接的帖子已更新,以确认这是 Oracle 的 jdk 中的错误。
【解决方案2】:

JavaBeans Specification 将 JavaBean 描述为:

Java Bean 是可重用的软件 可操作的组件 在构建器工具中可视化

它们需要提供自省、自定义、事件和持久性以及其他属性(第 2.1 节:什么是 bean?)

通常使用遵循 JavaBeans 规范(第 7.1 和 8.3 节)的访问器方法将“Java Bean”调用到普通的旧 Java 对象。事实上,这样的对象可能还远远不能满足所有要求。

如果您在此类中定义的对象实际上是一个 JavaBean,那么您的方法必须根据 JavaBean 规范第 7.1 节返回 void,其中访问器方法描述如下:

void setFoo(PropertyType value); // simple setter
PropertyType getFoo(); // simple getter

第 8.3 节命名为属性的设计模式说:

默认情况下,我们使用设计模式通过查找表单的方法来定位属性:

public <PropertyType> get<PropertyName>();
public void set<PropertyName>(<PropertyType> a);

此外,对于布尔属性,我们允许一个 getter 方法来匹配模式:

public boolean is<PropertyName>();

但是,如果您的类只是一个 POJO,那么使用您的方法链接策略没有任何问题,因为您可以偏离规范,因为您实际上并没有构建 JavaBean。毕竟,并非您定义的所有类都应该是 JavaBean,对吧?

您可能想看看Oracle JavaBeans Tutorial

【讨论】:

    【解决方案3】:

    没有理由你不能这样做。就个人而言,如果在对象创建期间使用了 setter,我会将它们命名为 withItem1() 和 withItem2()。

    ClassA obj = new ClassA().withItem1(item1).withItem2(item2);
    

    让它更清楚一点(无论如何对我来说)这些方法的意图是什么。

    【讨论】:

    • 您可能应该将它们传递给构造函数,并使用其中的设置器。也许你也想让二传手成为决赛。那会很清楚!
    • 伟大的汤姆。清晰,我喜欢。 @lpinto.eu 在构造函数中放置许多参数 - 或任何方法 - 从来都不是很清楚。
    【解决方案4】:

    检查Oracle JavaBean pages 后,我没有找到任何明确告诉您设置器需要无效的内容。尽管如此,所有示例都有无效的设置方法。

    Java API 中的PropertyDescriptor 支持非空设置器,所以我认为让你的设置器返回它应该是相当安全的。为了安全起见,您可能应该检查您打算使用的框架是否使用反射。比如没有Spring support non-void setters in xml config prior to version 3.1

    【讨论】:

    • 确实如此,Java Beans 规范第 7.1 节(访问器方法)表明 setter 方法应该是无效的。 (oracle.com/technetwork/java/javase/documentation/…)
    • Edalorzo 是对的,第 8.3 节更精确:“默认情况下,我们使用设计模式通过查找以下形式的方法来定位属性: public get(); public void set( a);"
    • 这里的简单setter示例显示了一个void setter方法,但第7.1节中没有任何内容表明setter必须是void。
    • @edalorzo 这些例子只是例子。 PropertyDescriptor 类允许将任何方法作为 setter 返回,并且规范中的任何地方都没有明确说明在这种情况下 setter 必须具有 void 返回类型。
    • @EdwinDalorzo 我的代码库完全依赖于java.beans.Introspector,它工作得很好。为什么?因为我所有的 Bean 类都有一个伴随的 BeanInfo 类。我所有的 bean 有返回 bean 本身而不是 void 的设置器。
    【解决方案5】:

    我猜这并没有违反 JavaBean 规范,虽然我不确定。

    查看以下示例:

    public class JavaBean {
    
        private String value;
    
        public String getValue() {
            return value;
        }
    
        public JavaBean setValue(String value) {
            this.value = value;
            return this;
        }
    
        public static void main(String[] args) throws Exception {
            JavaBean bean = new JavaBean();
            JavaBean.class.getMethod("setValue", String.class).invoke(bean, "test");
            System.out.println(bean.getValue());
        }
    }
    

    许多框架使用反射 API 访问 JavaBean。正如您在上面看到的,访问返回 'this' 的 settter 不受返回类型的影响(返回类型不用于通过反射定位方法)。这也是有道理的,因为在一个范围内不能有两个方法除了返回类型之外是相同的。

    【讨论】:

      【解决方案6】:

      对于使用 Spring 3.1+ 的人来说,这不再是问题了

      http://static.springsource.org/spring/docs/3.1.0.M2/spring-framework-reference/html/new-in-3.1.html

      【讨论】:

        【解决方案7】:

        是的。这是一种比较常见的技术,称为方法链,可用于创建“流畅的界面”。

        请参阅:http://en.wikipedia.org/wiki/Method_chaininghttp://en.wikipedia.org/wiki/Fluent_interface

        【讨论】:

        • 我个人不太愿意将这种方法链接的用法称为 Fluent Interface 模式的实例,恕我直言,一堆链接在一起的 setter 并不流畅,看起来太杂乱了。
        【解决方案8】:

        绝对没有什么可以阻止你这样做,但为什么。如果要执行此操作,请创建一个接受 args 的构造函数。请记住,某些使用 bean 的软件不会期望返回值,并且可能会产生一些意想不到的结果

        如果您只是想简化初始化,(也许是为了设置测试)您可以使用一些 groovy 代码。

        【讨论】:

          【解决方案9】:

          没有什么可以阻止您提供将目标对象作为接口中的约定返回的 setter 方法...

          然而,您还必须为Java Bean Simple Property setter 方法使用规范签名(例如void setProp(Type t)),否则需要该签名的其他软件将无法将bean 属性识别为可写。

          【讨论】:

          • 您确定您可以拥有名称和参数列表相同但返回类型不同的方法吗? AFAIK 这会造成歧义,因此是被禁止的
          【解决方案10】:

          Builder 模式通常用于构造不可变对象。尽管 JavaBeans 本质上不是不可变的,但我经常在我的 JavaBeans 上使用构建器模式,因为它提供了一个流利的接口,我可以在我的测试中使用它。在不破坏 JavaBean 规范的情况下,这两者很容易相互兼容。你可以在 Stack Overflow 上查看它:Builder Pattern in Effective Java

          您只需确保包含默认构造函数以及私有 Builder 构造函数,然后将标准 getter 和 setter 放入 JavaBean 对象中。

          这比构造函数链接更简洁,也更易于阅读。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多