Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Is it a good practice to use reflection if greatly reduces the quantity of boilerplate code?

Basically there is a trade-off between performance and maybe readability on one side and abstraction/automation/reduction of boilerplate code on the other side.

Edit: Here is an example of a recommended use of reflection.

To give an example, suppose there is a an abstract class Base which has 10 fields and has 3 subclasses SubclassA, SubclassB and SubclassC each with 10 different fields; they are all simple beans. The problem is that you get two Base type references and you want to see if their corresponding objects are of same (sub)type and are equal.

As solutions there is the raw solution in which you first check if the types are equal and then check all fields or you can use reflection and dynamically see if they are of the same type and iterate over all methods that start with "get" (convention over configuration), call them on both objects and call equals on the results.

boolean compare(Base base1, Base, base2) {
    if (base1 instanceof SubclassA && base2 instanceof SubclassA) { 
         SubclassA subclassA1 = (SubclassA) base1;
         SubclassA subclassA2 = (SubclassA) base2;
         compare(subclassA1, subclassA2);
    } else if (base1 instanceof SubclassB && base2 instanceof SubclassB) {
         //the same
    }
    //boilerplate
}

boolean compare(SubclassA subA1, SubclassA subA2) {
    if (!subA1.getField1().equals(subA2.getField1)) {
         return false;
    }
    if (!subA1.getField2().equals(subA2.getField2)) {
         return false;
    }
    //boilerplate
}

boolean compare(SubclassB subB1, SubclassB subB2) {
    //boilerplate
}

//boilerplate

//alternative with reflection 
boolean compare(Base base1, Base base2) {
        if (!base1.getClass().isAssignableFrom(base2.getClass())) {
            System.out.println("not same");
            System.exit(1);
        }
        Method[] methods = base1.getClass().getMethods();
        boolean isOk = true;
        for (Method method : methods) {
            final String methodName = method.getName();
            if (methodName.startsWith("get")) {
                Object object1 = method.invoke(base1);
                Object object2 = method.invoke(base2);
                if(object1 == null || object2 == null)  {
                    continue;
                }
                if (!object1.equals(object2)) {
                    System.out.println("not equals because " + object1 + " not equal with " + object2);
                    isOk = false;
                }
            }
        }

        if (isOk) {
            System.out.println("is OK");
        }
}
share|improve this question
7  
Overuse of anything is a bad habit. –  user61852 Apr 1 '13 at 12:49
    
@user61852 Right, too much freedom leads to dictatorship. Some old Greek already knew about this. –  ott-- Apr 1 '13 at 15:57
2  
“Too much water would be bad for you. Obviously, too much is precisely that quantity which is excessive—that’s what it means!” — Stephen Fry –  Jon Purdy Apr 1 '13 at 19:25
    
"Anytime you find yourself writing code of the form "if the object is of type T1, then do something, but if it's of type T2, then do something else," slap yourself. javapractices.com/topic/TopicAction.do?Id=31 –  rm5248 Apr 1 '13 at 21:48

2 Answers 2

Reflection was created for a specific purpose, to discover the functionality of a class that was unknown at compile time, similar to what the dlopen and dlsym functions do in C. Any use outside of that should be heavily scrutinized.

Did it ever occur to you that the Java designers themselves encountered this problem? That's why practically every class has an equals method. Different classes have different definitions of equality. In some circumstances a derived object could be equal to a base object. In some circumstances, equality could be determined based on private fields without getters. You don't know.

That's why every object who wants custom equality should implement an equals method. Eventually, you'll want to put the objects into a set, or use them as a hash index, then you'll have to implement equals anyway. Other languages do it differently, but Java uses equals. You should stick to the conventions of your language.

Also, "boilerplate" code, if put into the correct class, is pretty hard to screw up. Reflection adds additional complexity, meaning additional chances for bugs. In your method, for example, two objects are considered equal if one returns null for a certain field and the other doesn't. What if one of your getters returns one of your objects, without an appropriate equals? Your if (!object1.equals(object2)) will fail. Also making it bug prone is the fact that reflection is rarely used, so programmers aren't as familiar with its gotchas.

share|improve this answer

Overusing of reflection probably depends on the language used. Here you are using Java. In that case, reflection should be used with care because often it is only a workaround for bad design.

So, you are comparing different classes, this is a perfect problem for method overriding. Note that instances of two different classes should never be considered equal. You can compare for equality only if you have instances of the same class. See http://stackoverflow.com/questions/27581/overriding-equals-and-hashcode-in-java for an example how to implement equality comparison correctly.

share|improve this answer
7  
+1 - Some of us believe ANY use of reflection is a red flag indicating bad design. –  Ross Patterson Apr 1 '13 at 10:53
1  
Most probably in this case a solution based on equals is desirable, but as a general idea what is wrong with the reflection solution? Actually it is very generic and the equals methods do not need to be explicitly written in every class and subclass (although they can easily be generated by a good IDE). –  m3th0dman Apr 1 '13 at 11:00
2  
@RossPatterson Why? –  m3th0dman Apr 1 '13 at 11:00
3  
@m3th0dman It violates encapsulation - the base class has to access attributes in its subclasses. What if they are private (or getter private)? What about polymorphism? If I decide to add another subclass and I want to do the comparison differently in it? Well, the base class is already doing it for me and I can't change it. What if the getters lazy-load something? Do I want comparison method to do that? How do I even know a method starting with get is a getter and not a custom method returning something? –  Sulthan Apr 1 '13 at 11:15
1  
Note that some language features basically mandate the use of reflection, with annotations as a prime example. –  Xion Apr 1 '13 at 14:43

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.