17

can I define setter method to return this rather than void?

Like:

ClassA setItem1() {
      return this;
}

ClassA setItem2() {
      return this;
}

then I can use new ClassA().setItem1().setItem2()

p27
  • 2,217
  • 1
  • 27
  • 55
user705414
  • 20,472
  • 39
  • 112
  • 155

10 Answers10

15

There is a lot of misunderstanding about JavaBeans spec.

The main reason for it's existence is the unified Java "component" model. It's a way to interact programatically with a Java Object using Reflection. The API itself is named JavaBeans Introspection. Please, take a look at example usages and You will know a lot more than an average Java programmer does.

Introspection API can be used to manipulate GUI elements in an unified manner. Your component exposes it's properties as a pairs of getters and setters so that they could be discovered and manipulated at run-time on a GUI builder's property sheet.

So, mixing fluent APIs and JavaBeans Spec in my opinion is a no-go. That's two completely unrelated concepts and can disrupt each other. JavaBeans Introspection might not work when method signature differs (return type).

Take a look at this example (taken from linked tutorial):

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

This example creates a non-visual bean and displays following properties derived from the BeanInfo object:

  • class
  • name
  • size

You might want to see what happens when You change void return type to anything else. I have done so and the result is the same. So, does that mean it's allowed?

I'm afraid no. The JavaBeans spec is quite strict about those method signatures. It just happened that implementation is forgiving. Nonetheless, I'd disadvise mixing fluent interface with JavaBeans. You can't really rely that, if the discovery works now, it will also in future.

But, from the other side - it looks like You don't use JavaBeans to full extent. Only the getters/setters pair of method. It's up to You how You implement and design Your APIs.

Rekin
  • 9,731
  • 2
  • 24
  • 38
  • 3
    Nice sample, I'm surprised this actually works with non-void setters. One thing to look out for is that not all frameworks are using the Introspector and instead implement their own logic. So you might get different results when using for example EL, jpa or jaxb. – Jörn Horstmann Apr 21 '11 at 11:29
  • 3
    Can you quote any part of the JavaBeans spec that requires void return types from property setter methods? I'm not looking for examples. I'm looking for actual requirements of the specification that state that. Section 8.2 indicates that it is *not* a requirement. – jbindel May 30 '12 at 01:05
  • Take a look at the reflection api. Nowhere in the retrieval and calling of a reflective method is the return type ever taken into account. Method.invoke returns Object, and it never looks at the return type. It is not legal to have two methods with the same name and param signatures, thus, return type does not influence reflective method invocation. – Ajax Feb 01 '13 at 14:39
  • The Spec is exact about the method signature. You probably looked at Oracle's API and JVM. What about IBM and Azul ones? And gcj? Static code analysis tools and javac from Eclipse? They all have to rely on standard behaviour. It is up to You, whether you conform or not. If you're not conform than it would be harder to debug problems as in any non-standard solution. – Rekin Feb 01 '13 at 16:53
  • More down to earth examples include dynamic proxy generation (used for example in Hibernate) or Aspects. The fact is, you don't really know for sure that the methods will be called using a reflective API, which is similar to the one you analyzed. It might be, that the bytecode is analyzed to infer class structure. – Rekin Feb 01 '13 at 16:57
  • 2
    As of Java 7, the return type is being checked: http://stackoverflow.com/q/10806895/309259 – Rekin Aug 23 '13 at 17:57
  • 1
    @Rekin the post you linked was updated to confirm that it was a bug in Oracle's jdk. – Ravi Sanwal Nov 11 '16 at 01:24
13

The JavaBeans Specification describes a JavaBean as:

A Java Bean is a reusable software component that can be manipulated visually in a builder tool

They are required to provide introspection, customization, events and persistence among other properties (Section 2.1: What is a bean?)

It is common to call a "Java Bean" to a Plain Old Java Object with accessor methods following the JavaBeans Specification (Section 7.1 and 8.3). The truth is that such object could still be far from being compliant with all the requirements.

If the object your are defining in this class is actually a JavaBean then your method must return void according to JavaBean Specification, section 7.1 where accessor methods are described as follows:

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

The section 8.3 named designed patterns for properties says:

By default, we use design patterns to locate properties by looking for methods of the form:

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

In addition, for boolean properties, we allow a getter method to match the pattern:

public boolean is<PropertyName>();

However, if your class is just a POJO then there is nothing wrong with using your method chaining strategy because you are allowed to deviate from the specification since you are not actually building a JavaBean. Not all the classes you define are supposed to be JavaBeans after all, right?

Your might like to take a look at the Oracle JavaBeans Tutorial.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
9

No reason you couldn't do that. Personally, if the setters are being used during object creation, I'd name them withItem1() and withItem2().

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

Makes it a bit clearer (to me anyway) what the intent of the methods are.

Tom Jefferys
  • 13,090
  • 2
  • 35
  • 36
  • You should probably pass those to the constructor, and use the setters inside it. And probably you also want to make the setters final. That would be a lot clear! – lpinto.eu Nov 22 '12 at 15:14
  • Great Tom. Clear, I love it. @lpinto.eu to put many parameters in the constructor -or in any method- NEVER is something clear. – inigoD Jan 12 '16 at 15:11
5

After checking the Oracle JavaBean pages I didn't find anything which explicitly tells you that the setters need to be void. Nevertheless all examples have void set-methods.

The PropertyDescriptor in the Java API support non-void setters, so I think it should be pretty safe to let your setters return this. To be on the safe side you should probably check out if the frameworks you intend to use that uses reflection. For instance didn't Spring support non-void setters in xml config prior to version 3.1.

Aleksander Blomskøld
  • 18,374
  • 9
  • 76
  • 82
  • 4
    It actually does, the Java Beans Specification, section 7.1 (Accessor Methods) shows that the setter method should be void. (http://www.oracle.com/technetwork/java/javase/documentation/spec-136004.html) – Edwin Dalorzo Apr 21 '11 at 09:12
  • 1
    Edalorzo is right and the section 8.3 is even more precise: "By default, we use design patterns to locate properties by looking for methods of the form: public get(); public void set( a);" – gouessej Dec 06 '11 at 12:24
  • 1
    The example there for a simple setter shows a void setter method, but there is nothing in section 7.1 that the setter must be void. – jbindel May 29 '12 at 19:59
  • 1
    @edalorzo those examples are just examples. The PropertyDescriptor class allows any method to be returned as a setter, and nothing anywhere in the spec explicitly says that in that case setters must have `void` return types. – Pointy May 29 '12 at 22:41
  • @Pointy No, they are not. Try to do introspection of one of your beans using the `java.beans.Introspector` class and you will see fail if you do not comply with this requirement. – Edwin Dalorzo Jun 20 '12 at 19:42
  • 1
    @EdwinDalorzo my code base relies completely on `java.beans.Introspector` and it works just fine. Why? Because all my Bean classes have a companion BeanInfo class. All my beans *also* have setters that return the bean itself and not `void`. – Pointy Jun 20 '12 at 19:51
  • @Pointy However, the provision of a `BeanInfo` class is not a requirement in the specification. The spec does says in section 8.1 "By default we will use a low level reflection mechanism to study the methods supported by a target bean and then apply simple design patterns to deduce from those methods what properties, events, and public methods are supported.". Evidently, if you provide your own `BeanInfo` class you are allowed to name your methods the way you want, but that was not evident in your original argument. I think it is now clear. – Edwin Dalorzo Jun 20 '12 at 20:19
  • @EdwinDalorzo yes, exactly. Sorry if I didn't explain very well, but yes I have BeanInfo companions for that very reason! – Pointy Jun 20 '12 at 21:38
3

I would guess this is not in violation of the JavaBean specification, although I am not sure of it.

Check out the following example:

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

Many frameworks access JavaBeans using the reflection API. As you can see above, accessing a settter which returns 'this' is not influenced by the return type (the return type is not used to locate a method via reflection). It also makes sense, because you cannot have two methods in one scope that are identical except for their return type.

Adriaan Koster
  • 15,870
  • 5
  • 45
  • 60
2

Just to add that for people using Spring 3.1+ this is not an issue anymore

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

1

Yes. This is a somewhat common technique called Method Chaining, and can be used to create a "fluent interface".

See: http://en.wikipedia.org/wiki/Method_chaining, http://en.wikipedia.org/wiki/Fluent_interface

James Davies
  • 9,602
  • 5
  • 38
  • 42
  • 1
    I'm personally not comfortable calling this usage of method chaining as an instance of the Fluent Interface pattern, IMHO a bunch of setters chained together is not fluent, it would look too cluttered. – vickirk Apr 21 '11 at 08:32
1

Absolutely nothing to stop you doing that but why. If you want to do this create a constructor that takes the args. Bare in mind some software that uses beans would not be expecting return values and may have some unexpected results

If you just want to simplify initialisation, (maybe to set up tests) you could use some groovy code.

vickirk
  • 3,979
  • 2
  • 22
  • 37
0

The Builder pattern is generally used for constructing immutable objects. Even though JavaBeans by nature aren't immutable, I have frequently used the builder pattern on my JavaBeans because it provides a fluent interface that I can use in my tests. The two are easily compatible with each other without breaking the JavaBean specification. You can check out it out on Stack Overflow at Builder Pattern in Effective Java

You just need to make sure you include a default constructor as well as the private Builder constructor, and then put your standard getters and setters in the JavaBean object.

This is much cleaner than constructor chaining, and easier to read as well.

Community
  • 1
  • 1
0

There is nothing preventing you from providing setter methods which return the target object as a convention in your interface...

However, you must also use the canonical signature for Java Bean Simple Property setter methods (e.g. void setProp(Type t)) or the bean property will not be recognized as writeable by other software which expects that signature.

maerics
  • 151,642
  • 46
  • 269
  • 291
  • Are You sure You can have tho methods with same name and argument list, but different return types? AFAIK this imposes ambiguity and thus is forbidden – Rekin Apr 21 '11 at 09:05