I'm using BeanUtils to manipulate Java objects created via JAXB, and I've run into an interesting issue. Sometimes, JAXB will create a Java object like this:
public class Bean {
protected Boolean happy;
public Boolean isHappy() {
return happy;
}
public void setHappy(Boolean happy) {
this.happy = happy;
}
}
The following code works just fine:
Bean bean = new Bean();
BeanUtils.setProperty(bean, "happy", true);
However, attempting to get the happy property like so:
Bean bean = new Bean();
BeanUtils.getProperty(bean, "happy");
Results in this exception:
Exception in thread "main" java.lang.NoSuchMethodException: Property 'happy' has no getter method in class 'class Bean'
Changing everything to a primitive boolean allows both the set and get call to work. I don't have this option, however, since these are generated classes. I assume this happens because the Java Bean libraries only consider an is<name> method to represent a property if the return type is a primitive boolean, and not the wrapper type Boolean. Does anyone have a suggestion as to how to access properties like these through BeanUtils? Is there some kind of workaround I can use?
Finally I've found legal confirmation:
8.3.2 Boolean properties
In addition, for boolean properties, we allow a getter method to match the pattern:
public boolean is<PropertyName>();
From JavaBeans specification. Are you sure you haven't came across JAXB-131 bug?
Workaround to handle Boolean isFooBar() case with BeanUtils
Create new BeanIntrospector
private static class BooleanIntrospector implements BeanIntrospector{
#Override
public void introspect(IntrospectionContext icontext) throws IntrospectionException {
for (Method m : icontext.getTargetClass().getMethods()) {
if (m.getName().startsWith("is") && Boolean.class.equals(m.getReturnType())) {
String propertyName = getPropertyName(m);
PropertyDescriptor pd = icontext.getPropertyDescriptor(propertyName);
if (pd == null)
icontext.addPropertyDescriptor(new PropertyDescriptor(propertyName, m, getWriteMethod(icontext.getTargetClass(), propertyName)));
else if (pd.getReadMethod() == null)
pd.setReadMethod(m);
}
}
}
private String getPropertyName(Method m){
return WordUtils.uncapitalize(m.getName().substring(2, m.getName().length()));
}
private Method getWriteMethod(Class<?> clazz, String propertyName){
try {
return clazz.getMethod("get" + WordUtils.capitalize(propertyName));
} catch (NoSuchMethodException e) {
return null;
}
}
}
Register BooleanIntrospector:
BeanUtilsBean.getInstance().getPropertyUtils().addBeanIntrospector(new BooleanIntrospector());
you can just create second getter with SET - sufix as workaround :)
Related
This is a general issue/problem that I have come across. I wondered if anyone knows of any well suited design patterns or techniques.
private ExternalObject personObject;
private String name;
private int age;
private String address;
private String postCode;
public MyBuilderClass(ExternalObject obj)
this.personObject=obj;
build();
}
public build() {
setName(personObject.getName());
setAge(personObject.getAge());
setAddress(personObject.getAddress());
setPostCode(personObject.getPostCode());
.
.
. many more setters
}
The class above takes external objects from a queue and constructs MyBuilderClass objects.
A MyBuilderClass object is successfully built if all of the fields have been set to non-null non-empty values.
There will be many MyBuilderClass objects that cannot be built because data will be missing from the ExternalObject.
My problem, what is the best way to detect if an object has been correctly built?
I could check for null or empty values in the set methods and throw an exception. The problem with this approach is throwing exceptions is expensive and it will clogg the log files up because there will be many instances where an object cannot be built;
What other approaches could I use?
Correct me if I'm wrong: you are trying to find a good way to check if an object is valid, and if it is not, tell the client code about this without using an exception.
You can try a factory method:
private MyBuilderClass(ExternalObject obj)
this.personObject=obj;
build();
}
public static MyBuilderClass initWithExternalObject(ExternalObject obj) {
// check obj's properties...
if (obj.getSomeProperty() == null && ...) {
// invalid external object, so return null
return null;
} else {
// valid
MyBuilderClass builder = new MyBuilderClass(obj);
return builder.build();
}
}
Now you know whether an object is valid without using an exception. You just need to check whether the value returned by initWithExternalObject is null.
I wouldn't throw exceptions in cases that aren't exceptional. And as the only way for a constructor not to produce an object is to throw, you should not delay validation to the constructor.
I'd still recommend the constructor to throw if its results were to be invalid, but there should be a validation before that, so you don't even call the constructor with an invalid ExternalObject.
It's up to you if you want to implement that as a static method boolean MyBuilderClass.validate(ExternalObject) or by using the builder pattern with this validation.
Another approach for such a validation is to use java Annotations:
Make a simple annotaion class, let's say Validate:
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#interface Validate {
boolean required() default true;
}
then annotate the fields you want to be present as #Validate(required=true):
class MyBuilderClass {
private ExternalObject externalObject;
#Validate(required=true)
private String name;
#Validate(required=false) /*since it's a primitive field*/
private int age;
#Validate(required=true)
private String address;
#Validate(required=true)
private String postCode;
MyBuilderClass(ExternalObject externalObject) {
this.externalObject = externalObject;
build();
}
public void build() {
setName(personObject.getName());
setAge(personObject.getAge());
setAddress(personObject.getAddress());
setPostCode(personObject.getPostCode());
}
//.
//.
//. many more setters
}
And then add this method in the MyBuilderClass class, in order to check if your Object is built correctly:
public boolean isCorrectlyBuilt() throws IllegalAccessException {
boolean retVal = true;
for (Field f : getClass().getDeclaredFields()) {
f.setAccessible(true);
boolean isToBeChecked = f.isAnnotationPresent(Validate.class);
if (isToBeChecked) {
Validate validate = f.getAnnotation(Validate.class);
if (validate.required()/*==true*/) {
if (f.get(this) == null) {
retVal = false;
break;
/* return false; */
}
}
}
}
return retVal;
}
Here is an example of use :
public static void main(String[] args) throws Exception {
ExternalObject personObject = new ExternalObject();
personObject.setAge(20);
personObject.setName("Musta");
personObject.setAddress("Home");
personObject.setPostCode("123445678");
MyBuilderClass myBuilderClass = new MyBuilderClass(personObject);
System.out.println(myBuilderClass.isCorrectlyBuilt());
}
Output : true because the object is correctly built.
This will allow you to choose the fields that you want to be in the structure by reflection, without bringing those inherited from a base class.
As this previous answer suggests, here are 2 options either of which should be added after you have tried to set the variables.
use reflection to check whether any of the variables are null. (As mentioned in comments this will check all fields in this object but be careful with fields in any superclasses).
public boolean checkNull() throws IllegalAccessException {
for (Field f : getClass().getDeclaredFields())
if (f.get(this) != null)
return false;
return true;
}
perform a null check on each variable.
boolean isValidObject = !Stream.of(name, age, ...).anyMatch(Objects::isNull);
Previous answer
From what I've come across you could overwrite the equals method of your object and compare it with a valid example object. Its dirty and might only work in some cases.
Your approach is the best I could think of. Write a seperate method or class that has for example a static validate method. You could reuse it anywhere.
I recently moved my spring application from java 1.6 to java 1.8. This has caused the spring bootstrapping to take an order of magnitude longer (20s before, 4mins now). Tracing the cause has led me to the CachedIntrospectionResults class, which is created for every bean. When created it calls,
beanInfo = (shouldIntrospectorIgnoreBeaninfoClasses ?
Introspector.getBeanInfo(beanClass, Introspector.IGNORE_ALL_BEANINFO) :
Introspector.getBeanInfo(beanClass));
Introspector then creates the bean info, in java 1.6, it calls
private BeanDescriptor getTargetBeanDescriptor() {
// Use explicit info, if available,
if (explicitBeanInfo != null) {
BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();
if (bd != null) {
return (bd);
}
}
// OK, fabricate a default BeanDescriptor.
return new BeanDescriptor(this.beanClass);
}
However in java 1.8 it now calls,
private BeanDescriptor getTargetBeanDescriptor() {
// Use explicit info, if available,
if (explicitBeanInfo != null) {
BeanDescriptor bd = explicitBeanInfo.getBeanDescriptor();
if (bd != null) {
return (bd);
}
}
// OK, fabricate a default BeanDescriptor.
return new BeanDescriptor(this.beanClass, findCustomizerClass(this.beanClass));
}
private static Class<?> findCustomizerClass(Class<?> type) {
String name = type.getName() + "Customizer";
try {
type = ClassFinder.findClass(name, type.getClassLoader());
// Each customizer should inherit java.awt.Component and implement java.beans.Customizer
// according to the section 9.3 of JavaBeans™ specification
if (Component.class.isAssignableFrom(type) && Customizer.class.isAssignableFrom(type)) {
return type;
}
}
catch (Exception exception) {
// ignore any exceptions
}
return null;
}
This method as far as I can see was added with java 1.7, and since I don't define any customizer classes, it searches my full classpath then throws an exception which ends up taking a few hundred ms. The result being that each bean takes ~500ms to init. A huge hit to startup time.
I am now trying to find a way to work around this problem,
The spring documentation says to implement a BeanInfoFactory in order to customize the beanInfo creation. But I can't find anywhere that says how to actaually create BeanInfo for a provided class.
How would I actually do that? Introspector uses a bunch of private constructors to build it up so I can't really follow it, and simply returning an empty BeanInfo blows spring up. What does spring actually want with the beaninfo?
Any ideas?
Normally, when you provide an explicit BeanInfo, the Introspector will gather information automatically whenever the explicit BeanInfo returns null. So there should be no problem providing an empty BeanInfo that only returns a non-null BeanDescriptor to prohibit the automatic Customizer search.
For example:
import java.beans.*;
import java.util.stream.Stream;
public class BeanInfoTest {
public static void main(String... arg) throws IntrospectionException {
BeanInfo bi=Introspector.getBeanInfo(TheComponent.class, Object.class);
System.out.println("properties: ");
Stream.of(bi.getPropertyDescriptors())
.map(p->p.getPropertyType().getSimpleName()+' '+p.getName())
.forEach(System.out::println);
}
public static class TheComponent {
String foo;
int bar;
public String getFoo() {
return foo;
}
public void setFoo(String foo) {
this.foo = foo;
}
public int getBar() {
return bar;
}
public void setBar(int bar) {
this.bar = bar;
}
}
public static class TheComponentBeanInfo extends SimpleBeanInfo {
/** Overridden to prevent the automated search for a Customizer */
#Override
public BeanDescriptor getBeanDescriptor() {
System.out.println("Providing my explicit BeanDescriptor");
return new BeanDescriptor(TheComponent.class);
}
}
}
will print
Providing my explicit BeanDescriptor
properties:
int bar
String foo
So it found the properties using automated search while using the explicit BeanDescriptor.
I'm currently revising a web application, and I have questions about data binding. I have a method that has been mapped with #RequestMapping, and in one of it's arguments I have a primitive integer type, something like this (the following code is basically a summary of my problem, not the actual code):
#RequestMapping(value = "/processSomething" , method = RequestMethod.GET)
public String processSomething(#ModelAttribute("myValue") int myValue)
{
// Do something with "myValue".
}
When I run the web application, i get the following:
HTTP Status 500 - Request processing failed; nested exception is
org.springframework.beans.BeanInstantiationException: Could not
instantiate bean class [int]: No default constructor found; nested
exception is java.lang.NoSuchMethodException: int.()
It makes me realize that data binding works only with objects. I tried to change int with Integer, but I ended up getting something very similar:
HTTP Status 500 - Request processing failed; nested exception is
org.springframework.beans.BeanInstantiationException: Could not
instantiate bean class [java.lang.Integer]: No default constructor
found; nested exception is java.lang.NoSuchMethodException:
java.lang.Integer.()
I know #ModelAttribute allows us to make data binding with the Spring MVC model, and if a model is not in there, it is created automatically by Spring and then it's returned. What am I doing wrong? What I forgot to do? Do I need to create a PropertyEditor for primitive types?
The funny thing is that it works perfectly with #RequestParam, but I would not want the user to see the value of my property in the URL.
NOTE: I'm currently using Spring Web MVC 4.1.1.RELEASE (with MAVEN)
UPDATE
I did what was suggested by tofindabhishek user. I created a class with the name Inteiro (translated as Integer), and I am using it as #ModelAttribute, just like this:
#RequestMapping(value = "/usuarios" , method = RequestMethod.GET)
public String getUsuarios(
Model model ,
#RequestParam("pag") int pagina ,
#ModelAttribute("total") Inteiro registros ,
#ModelAttribute("pesquisa") CriterioBuilder criterio ,
#ModelAttribute("id_sexo_f") Inteiro idSexF ,
#ModelAttribute("id_grupo_adm") Inteiro idGrpAdm )
{
// ...
}
The Inteiro class has basically just a single int primitive field with a public and empty constructor, and a set, get, equals and hashCode method. The previous problem appears to have been resolved, but when running my application, I came across this:
HTTP Status 500 - javax.el.ELException: Cannot convert
com.regra7.minhaapp.controle.wrap.Inteiro#3b of type class
com.regra7.minhaapp.controle.wrap.Inteiro to class java.lang.Long
Here's the Inteiro source code:
public class Inteiro
{
// #############################################################################################
// INSTÂNCIAS
// #############################################################################################
private int valor;
// #############################################################################################
// CONSTRUTORES
// #############################################################################################
public Inteiro()
{
this.valor = 0;
}
// #############################################################################################
// MODIFICADORES
// #############################################################################################
public void set(int valor) { this.valor = valor; }
// #############################################################################################
// ACESSO
// #############################################################################################
public int get() { return this.valor; }
// #############################################################################################
// EQUALS E HASHCODE
// #############################################################################################
#Override
public boolean equals(Object o)
{
if (o == null)
{
return false;
}
else if (o == this)
{
return true;
}
else if (o.getClass() != this.getClass())
{
return false;
}
Inteiro inteiro = (Inteiro) o;
return inteiro.get() == valor;
}
#Override
public int hashCode()
{
return valor;
}
}
For what reason Spring is complaining that can not convert Inteiro to java.lang.Long? I'm not working with Long. Moreover... EL? That would mean "Expression Language", right? Does this have something to do with some of my JSP pages? I am trying to develop a JSP page that displays search results, and on this page I use EL. Is there any possibility to be a problem in my JSP page?
Thank you for your help.
public class ViewModel {
private Integer myValue;
}
Use Wrapper object(ViewModel) to capture your value and Bind ViewModel class as model attribute in this case you can handle null values by using Wrapper(Integer).if you don't want to handle null values you can use int.
public class ViewModel {
private int myValue;
}
I have a custom implementation of an AbstractSingleBeanDefinitionParser to allow me to define 3D Vectors in my spring config with less... ceremony than would otherwise be required.
<rbf:vector3d id="test_vector" delimeter=";" value="45;46;47"/>
That works great, and I have been using it for months without any problems. Yesterday I tried to define the value in a .properties file like this:
In test.properties I have:
vector3d.value=1,2,3
And in the xml file I have:
<context:property-placeholder location="test.properties"/>
<rbf:vector3d id="test_vector_with_properties" delimeter="," value="${vector3d.value}"/>
When I try to run my unit test, it crashes, and I get this exception:
Caused by: java.lang.NumberFormatException: For input string: "${vector3d.value}"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1222)
at java.lang.Double.parseDouble(Double.java:510)
at scala.collection.immutable.StringLike$class.toDouble(StringLike.scala:234)
at scala.collection.immutable.StringOps.toDouble(StringOps.scala:31)
at rb.foundation.spring.xml.Vector3DBeanDefinitionParser$$anonfun$1.apply(Vector3DBeanDefinitionParser.scala:25)
When I use the .properties file for normal beans, it works great, which leads me to believe that there is a subtlety that I overlooked in my implemention of my parser. It's written in scala, but you should be able to follow it:
class Vector3DBeanDefinitionParser extends AbstractSingleBeanDefinitionParser
{
override def getBeanClass(element : Element) = classOf[Vector3D]
override def doParse(element: Element, builder: BeanDefinitionBuilder)
{
val delim = element.getAttribute("delimeter")
val value = element.getAttribute("value")
val values = value.split(delim).map(_.toDouble)
builder.addConstructorArgValue(values(0))
builder.addConstructorArgValue(values(1))
builder.addConstructorArgValue(values(2))
}
}
I'm happy to add the key substitution if necessary, I just need to know where/how to do it.
Ideas?
So the reason this doesn't work is that your BeanDefinitionParser runs much before property placeholders are resolved. Simple overview as I understand it:
BeanDefinitionParsers parse the XML into BeanDefinition objects in memory
BeanDefinitions are then loaded into a BeanFactory
BeanFactoryPostProcessors (including the property placeholder configurers) are executed on the bean definitions
beans are created from the bean definitions
(Of course other things happen along the way, but those are the relevant steps here.)
So in order to get the resolved property value into your Vector3D object, I think you're going to have to delay specifying the arguments to the Vector3D constructor until after BeanFactoryPostProcessors have run. One way that occurs to me is to have your BeanDefinitionParser construct a bean definition for a Spring FactoryBean instead of the Vector3D itself. Then the splitting of the vector value that you currently have in your Vector3DBeanDefinitionParser would need to be in the FactoryBean implementation instead.
Sorry, I'm not too familiar with Scala so this will be in Java.
The FactoryBean class would look something like this:
import org.springframework.beans.factory.FactoryBean;
public class Vector3DFactoryBean implements FactoryBean<Vector3D> {
private String delimiter;
private String value;
private transient Vector3D instance;
public String getDelimiter() { return delimiter; }
public void setDelimiter(String delimiter) { this.delimiter = delimiter; }
public String getValue() { return value; }
public void setValue(String value) { this.value = value; }
#Override
public Vector3D getObject() {
if (instance == null) {
String[] values = value.split(delimiter);
instance = new Vector3D(
Double.parseDouble(values[0]),
Double.parseDouble(values[1]),
Double.parseDouble(values[2])
);
}
return instance;
}
#Override
public Class<?> getObjectType() {
return Vector3D.class;
}
#Override
public boolean isSingleton() {
return true;
}
}
Then your Vector3DBeanDefinitionParser would just pass the delimiter and value untouched to the Vector3DFactoryBean bean definition:
class Vector3DBeanDefinitionParser extends AbstractSingleBeanDefinitionParser
{
override def getBeanClass(element : Element) = classOf[Vector3DFactoryBean]
override def doParse(element: Element, builder: BeanDefinitionBuilder)
{
val delim = element.getAttribute("delimeter")
val value = element.getAttribute("value")
builder.addPropertyValue("delimiter", delim)
builder.addPropertyValue("value", value)
}
}
Then later when the placeholder property configurer runs, it should resolve the property values in the Vector3DFactoryBean bean definition. When beans are finally created from bean definitions, the Vector3DFactoryBean will parse the vector values and create the Vector3D object.
This is the second time I found myself writing this kind of code, and decided that there must be a more readable way to accomplish this:
My code tries to figure something out, that's not exactly well defined, or there are many ways to accomplish it. I want my code to try out several ways to figure it out, until it succeeds, or it runs out of strategies. But I haven't found a way to make this neat and readable.
My particular case: I need to find a particular type of method from an interface. It can be annotated for explicitness, but it can also be the only suitable method around (per its arguments).
So, my code currently reads like so:
Method candidateMethod = getMethodByAnnotation(clazz);
if (candidateMethod == null) {
candidateMethod = getMethodByBeingOnlyMethod(clazz);
}
if (candidateMethod == null) {
candidateMethod = getMethodByBeingOnlySuitableMethod(clazz);
}
if (candidateMethod == null) {
throw new NoSuitableMethodFoundException(clazz);
}
There must be a better way…
Edit: The methods return a method if found, null otherwise. I could switch that to try/catch logic, but that hardly makes it more readable.
Edit2: Unfortunately, I can accept only one answer :(
To me it is readable and understandable. I'd simply extract the ugly part of the code to a separate method (following some basic principles from "Robert C.Martin: Clean Code") and add some javadoc (and apologies, if necessary) like that:
//...
try {
Method method = MethodFinder.findMethodIn(clazz);
catch (NoSuitableMethodException oops) {
// handle exception
}
and later on in MethodFinder.java
/**
* Will find the most suitable method in the given class or throw an exception if
* no such method exists (...)
*/
public static Method findMethodIn(Class<?> clazz) throws NoSuitableMethodException {
// all your effort to get a method is hidden here,
// protected with unit tests and no need for anyone to read it
// in order to understand the 'main' part of the algorithm.
}
I think for a small set of methods what you're doing is fine.
For a larger set, I might be inclined to build a Chain of Responsibility, which captures the base concept of trying a sequence of things until one works.
I don't think that this is such a bad way of doing it. It is a bit verbose, but it clearly conveys what you are doing, and is easy to change.
Still, if you want to make it more concise, you can wrap the methods getMethod* into a class which implements an interface ("IMethodFinder") or similar:
public interface IMethodFinder{
public Method findMethod(...);
}
Then you can create instances of you class, put them into a collection and loop over it:
...
Method candidateMethod;
findLoop:
for (IMethodFinder mf: myMethodFinders){
candidateMethod = mf.findMethod(clazz);
if (candidateMethod!=null){
break findLoop;
}
}
if (candidateMethod!=null){
// method found
} else {
// not found :-(
}
While arguably somewhat more complicated, this will be easier to handle if you e.g. need to do more work between calling the findMethods* methods (such as more verification that the method is appropriate), or if the list of ways to find methods is configurable at runtime...
Still, your approach is probably OK as well.
I'm sorry to say, but the method you use seems to be the widely accepted one. I see a lot of code like that in the code base of large libraries like Spring, Maven etc.
However, an alternative would be to introduce a helper interface that can convert from a given input to a given output. Something like this:
public interface Converter<I, O> {
boolean canConvert(I input);
O convert(I input);
}
and a helper method
public static <I, O> O getDataFromConverters(
final I input,
final Converter<I, O>... converters
){
O result = null;
for(final Converter<I, O> converter : converters){
if(converter.canConvert(input)){
result = converter.convert(input);
break;
}
}
return result;
}
So then you could write reusable converters that implement your logic. Each of the converters would have to implement the canConvert(input) method to decide whether it's conversion routines will be used.
Actually: what your request reminds me of is the Try.these(a,b,c) method in Prototype (Javascript).
Usage example for your case:
Let's say you have some beans that have validation methods. There are several strategies to find these validation methods. First we'll check whether this annotation is present on the type:
// retention, target etc. stripped
public #interface ValidationMethod {
String value();
}
Then we'll check whether there's a method called "validate". To make things easier I assume, that all methods define a single parameter of type Object. You may choose a different pattern. Anyway, here's sample code:
// converter using the annotation
public static final class ValidationMethodAnnotationConverter implements
Converter<Class<?>, Method>{
#Override
public boolean canConvert(final Class<?> input){
return input.isAnnotationPresent(ValidationMethod.class);
}
#Override
public Method convert(final Class<?> input){
final String methodName =
input.getAnnotation(ValidationMethod.class).value();
try{
return input.getDeclaredMethod(methodName, Object.class);
} catch(final Exception e){
throw new IllegalStateException(e);
}
}
}
// converter using the method name convention
public static class MethodNameConventionConverter implements
Converter<Class<?>, Method>{
private static final String METHOD_NAME = "validate";
#Override
public boolean canConvert(final Class<?> input){
return findMethod(input) != null;
}
private Method findMethod(final Class<?> input){
try{
return input.getDeclaredMethod(METHOD_NAME, Object.class);
} catch(final SecurityException e){
throw new IllegalStateException(e);
} catch(final NoSuchMethodException e){
return null;
}
}
#Override
public Method convert(final Class<?> input){
return findMethod(input);
}
}
// find the validation method on a class using the two above converters
public static Method findValidationMethod(final Class<?> beanClass){
return getDataFromConverters(beanClass,
new ValidationMethodAnnotationConverter(),
new MethodNameConventionConverter()
);
}
// example bean class with validation method found by annotation
#ValidationMethod("doValidate")
public class BeanA{
public void doValidate(final Object input){
}
}
// example bean class with validation method found by convention
public class BeanB{
public void validate(final Object input){
}
}
You may use Decorator Design Pattern to accomplish different ways of finding out how to find something.
public interface FindMethod
{
public Method get(Class clazz);
}
public class FindMethodByAnnotation implements FindMethod
{
private final FindMethod findMethod;
public FindMethodByAnnotation(FindMethod findMethod)
{
this.findMethod = findMethod;
}
private Method findByAnnotation(Class clazz)
{
return getMethodByAnnotation(clazz);
}
public Method get(Class clazz)
{
Method r = null == findMethod ? null : findMethod.get(clazz);
return r == null ? findByAnnotation(clazz) : r;
}
}
public class FindMethodByOnlyMethod implements FindMethod
{
private final FindMethod findMethod;
public FindMethodByOnlyMethod(FindMethod findMethod)
{
this.findMethod = findMethod;
}
private Method findByOnlyMethod(Class clazz)
{
return getMethodOnlyMethod(clazz);
}
public Method get(Class clazz)
{
Method r = null == findMethod ? null : findMethod.get(clazz);
return r == null ? findByOnlyMethod(clazz) : r;
}
}
Usage is quite simple
FindMethod finder = new FindMethodByOnlyMethod(new FindMethodByAnnotation(null));
finder.get(clazz);
... I could switch that to try/catch logic, but that hardly makes it more readable.
Changing the signature of the get... methods so you can use try / catch would be a really bad idea. Exceptions are expensive and should only be used for "exceptional" conditions. And as you say, the code would be less readable.
What is bothering you is the repeating pattern used for flow control--and it should bother you--but there isn't too much to be done about it in Java.
I get really annoyed at repeated code & patterns like this, so for me it would probably be worth it to extract the repeated copy & paste control code and put it in it's own method:
public Method findMethod(Class clazz)
int i=0;
Method candidateMethod = null;
while(candidateMethod == null) {
switch(i++) {
case 0:
candidateMethod = getMethodByAnnotation(clazz);
break;
case 1:
candidateMethod = getMethodByBeingOnlyMethod(clazz);
break;
case 2:
candidateMethod = getMethodByBeingOnlySuitableMethod(clazz);
break;
default:
throw new NoSuitableMethodFoundException(clazz);
}
return clazz;
}
Which has the disadvantage of being unconventional and possibly more verbose, but the advantage of not having as much repeated code (less typos) and reads easier because of there being a little less clutter in the "Meat".
Besides, once the logic has been extracted into it's own class, verbose doesn't matter at all, it's clarity for reading/editing and for me this gives that (once you understand what the while loop is doing)
I do have this nasty desire to do this:
case 0: candidateMethod = getMethodByAnnotation(clazz); break;
case 1: candidateMethod = getMethodByBeingOnlyMethod(clazz); break;
case 2: candidateMethod = getMethodByBeingOnlySuitableMethod(clazz); break;
default: throw new NoSuitableMethodFoundException(clazz);
To highlight what's actually being done (in order), but in Java this is completely unacceptable--you'd actually find it common or preferred in some other languages.
PS. This would be downright elegant (damn I hate that word) in groovy:
actualMethod = getMethodByAnnotation(clazz) ?:
getMethodByBeingOnlyMethod(clazz) ?:
getMethodByBeingOnlySuitableMethod(clazz) ?:
throw new NoSuitableMethodFoundException(clazz) ;
The elvis operator rules. Note, the last line may not actually work, but it would be a trivial patch if it doesn't.