views:

435

answers:

3

OK, after reviewing some code with PMD and FindBugs code analyzers, i was able to do great changes on the reviewed code. However, there are some things i don't know how to fix. I'll iterate them bellow, and (for better reference) i will give each question a number. Feel free to answer to any/all of them. Thanks for your patience.

1. Even tough i have removed some of the rules, the associated warnings are still there, after re-evaluate the code. Any idea why?


2. Please look at the declarations :

    private Combo comboAdress;

    private ProgressBar pBar;

and the references to objects by getters and setters :

    private final Combo getComboAdress() {
  return this.comboAdress;
 }

 private final void setComboAdress(final Combo comboAdress) {
  this.comboAdress = comboAdress;
 }

 private final ProgressBar getpBar() {
  return this.pBar;
 }

 private final void setpBar(final ProgressBar pBar) {
   this.pBar = pBar;
 }

Now, i wonder why the first declaration don't give me any warning on PMD, while the second gives me the following warning :

Found non-transient, non-static member. Please mark as transient or provide accessors.

More details on that warning here.


3. Here is another warning, also given by PMD :

    A method should have only one exit point, and that should be the last statement in the method

More details on that warning here.

Now, i agree with that, but what if i write something like this :

public void actionPerformedOnModifyComboLocations() {
    if (getMainTree().isFocusControl()) {
        return;
    }
    ....//do stuffs, based on the initial test
}

I tend to agree with the rule, but if performance of the code suggest multiple exit points, what should i do?


4. PMD gives me this :

Found 'DD'-anomaly for variable 'start_page' (lines '319'-'322').

when i declare something like :

String start_page = null;

I get rid of this info (level of warning is info) if i remove the assignment to null, but..i got an error from IDE, saying that the variable could be uninitialized, at some point later in the code. So, i am kind of stuck with that. Supressing the warning is the best you can do?


5. PMD Warning :

Assigning an Object to null is a code smell.  Consider refactoring.

This is the case of a singletone use of GUI components or the case of a method who returns complex objects. Assigning the result to null in the catch() section it's justified by the need to avoid the return of an incomplete/inconsistent object. Yes, NullObject should be used, but there are cases where i don't want to do that. Should i supress that warning then?


6. FindBugs warning #1:

Write to static field MyClass.instance from instance method MyClass.handleEvent(Event)

in the method

@Override
public void handleEvent(Event e) {
    switch (e.type) {
        case SWT.Dispose: {
            if (e.widget == getComposite()) {
                MyClass.instance = null;
            }
       break;
         }
    }
}

of the static variable

private static MyClass instance = null;

The variable allows me to test whether the form is already created and visible or not, and i need to force the re-creation of the form, in some cases. I see no other alternative here. Any insights? (MyClass implements Listener, hence the overrided handleEvent() method).


7. FindBugs warning #2:

Class MyClass2 has a circular dependency with other classes

This warning is displayed based on simple imports of other classes. Do i need to refactor those imports to make this warning go away? Or the problem relies in MyClass2?

OK, enough said for now..expect an update, based on more findings and/or your answers. Thanks.

+1  A: 
  1. I have no idea. It seems likely that whatever you did do, it was not what you were attempting to do!

  2. Perhaps the declarations appear in a Serializable class but that the type (e.g. ComboProgress are not themselves serializable). If this is UI code, then that seems very likely. I would merely comment the class to indicate that it should not be serialized.

  3. This is a valid warning. You can refactor your code thus:

    public void actionPerformedOnModifyComboLocations() {
        if (!getMainTree().isFocusControl()) {
            ....//do stuffs, based on the initial test
        }
    }
    
  4. This is why I can't stand static analysis tools. A null assignment obviously leaves you open to NullPointerExceptions later. However, there are plenty of places where this is simply unavoidable (e.g. using try catch finally to do resource cleanup using a Closeable)

  5. This also seems like a valid warning and your use of static access would probably be considered a code smell by most developers. Consider refactoring via using dependency-injection to inject the resource-tracker into the classes where you use the static at the moment.

  6. If your class has unused imports then these should be removed. This might make the warnings disappear. On the other hand, if the imports are required, you may have a genuine circular dependency, which is something like this:

    class A {
        private B b;
    }
    class B {
        private A a;
    }
    

This is usually a confusing state of affairs and leaves you open to an initialization problem. For example, you may accidentally add some code in the initialization of A that requires its B instance to be initialized. If you add similar code into B, then the circular dependency would mean that your code was actually broken (i.e. you couldn't construct either an A or a B.

Again an illustration of why I really don't like static analysis tools - they usually just provide you with a bunch of false positives. The circular-dependent code may work perfectly well and be extremely well-documented.

oxbow_lakes
Wow, thanks for the reply. Yes, @ point 3, i could use that..but what if i will have 15 conditions to check? That will make me use 15 x if {...} branches..which is not pretty. Regarding your answer on 6 : can u detail that "dependency-injection" technique? I am very interested in that..a link or example will me much appreciated. +1 for the speed of response and the introduction of a new notion to me. Thanks.
Hypercube
OK, that was stupid to ask..i've google it and seems like a well-documented technique :-). If u have some bookmark of a great tutorial, feel free to provide it, otherwise, no probs.
Hypercube
Regarding your answer on 7), i don't have unused imports. I've configured my IDE to do that sort on the save action of the edited code. Along with formatting the code and other goodies..so..my 22 warnings on the matter must be genuine circular dependencies..i wonder how to get rid of them :-)..but..i guess i'll have to figure that out myself..as a quick answer cannot be provided without actually seen the code.
Hypercube
For 3, very few developers obey Single Entry Single Exit (SESE) (it seems the mostly have formal reasoning backgrounds). For 4, you don't need `null`s with sensible resource handling.
Tom Hawtin - tackline
@Tom - what you say about `null` and sensible resource handling is just wrong: you can't properly handle reading from a file without doing this. Of course, you might hide this in a library method, but it is still there.
oxbow_lakes
+1  A: 

Here are my answers to some of your questions:


Question number 2:

I think you're not capitalizing the properties properly. The methods should be called getPBar and setPBar.

String pBar;

void setPBar(String str) {...}
String getPBar() { return pBar};

The JavaBeans specification states that:

For readable properties there will be a getter method to read the property value. For writable properties there will be a setter method to allow the property value to be updated. [...] Constructs a PropertyDescriptor for a property that follows the standard Java convention by having getFoo and setFoo accessor methods. Thus if the argument name is "fred", it will assume that the reader method is "getFred" and the writer method is "setFred". Note that the property name should start with a lower case character, which will be capitalized in the method names.


Question number 3:

I agree with the suggestion of the software you're using. For readability, only one exit point is better. For efficiency, using 'return;' might be better. My guess is that the compiler is smart enough to always pick the efficient alternative and I'll bet that the bytecode would be the same in both cases.

FURTHER EMPIRICAL INFORMATION

I did some tests and found out that the java compiler I'm using (javac 1.5.0_19 on Mac OS X 10.4) is not applying the optimization I expected.

I used the following class to test:

public abstract class Test{

  public int singleReturn(){   
     int ret = 0;
     if (cond1())
         ret = 1;
     else if (cond2())
         ret = 2;
     else if (cond3())
         ret = 3;

    return ret;
  }

  public int multReturn(){
    if (cond1()) return 1;
    else if (cond2()) return 2;
    else if (cond3()) return 3;
    else return 0;
  }

  protected abstract boolean cond1();
  protected abstract boolean cond2();
  protected abstract boolean cond3();
}

Then, I analyzed the bytecode and found that for multReturn() there are several 'ireturn' statements, while there is only one for singleReturn(). Moreover, the bytecode of singleReturn() also includes several goto to the return statement.

I tested both methods with very simple implementations of cond1, cond2 and cond3. I made sure that the three conditions where equally provable. I found out a consistent difference in time of 3% to 6%, in favor of multReturn(). In this case, since the operations are very simple, the impact of the multiple return is quite noticeable.

Then I tested both methods using a more complicated implementation of cond1, cond2 and cond3, in order to make the impact of the different return less evident. I was shocked by the result! Now multReturn() is consistently slower than singleReturn() (between 2% and 3%). I don't know what is causing this difference because the rest of the code should be equal.

I think these unexpected results are caused by the JIT compiler of the JVM.

Anyway, I stand by my initial intuition: the compiler (or the JIT) can optimize these kind of things and this frees the developer to focus on writing code that is easily readable and maintainable.


Question number 6:

You could call a class method from your instance method and leave that static method alter the class variable.

Then, your code look similar to the following:

public static void clearInstance() {
    instance = null;
}

@Override
public void handleEvent(Event e) {
    switch (e.type) {
        case SWT.Dispose: {
            if (e.widget == getComposite()) {
                MyClass.clearInstance();
            }
       break;
         }
    }
}

This would cause the warning you described in 5, but there has to be some compromise, and in this case it's just a smell, not an error.


Question number 7:

This is simply a smell of a possible problem. It's not necessarily bad or wrong, and you cannot be sure just by using this tool.

If you've got a real problem, like dependencies between constructors, testing should show it.

A different, but related, problem are circular dependencies between jars: while classes with circular dependencies can be compiled, circular dependencies between jars cannot be handled in the JVM because of the way class loaders work.

nozebacle
Wow..that was amazing. It fixed the problem with pBar..and i guess the other warnings are given from the same reason. But..that is how Eclipse suggested the getters and setters should be created and..it's a bit stupid, dont you think? I mean, those methods ARE the get and set method for the object, regardless of their name :-(. +1 for the observation.
Hypercube
If Eclipse suggests that, shame on them! The Java Beans standard is clear about capitalizing the first letter of the field in the getters and setters.You cannot 'mark' methods as the getters and the setters, you've got to rely on their name, and they were not getX / setX but getpX / setpX.
nozebacle
This : public static void setNullInstance() { Dog.instance = null; }inside my Dog class will do the trick,and leaves me with code-smell warning for assigning object to null..which is not good enough. Using private instance method is NOT an option, since the instance might be null. Yes, i could check for that..but..things get complicated..for no reason. There MUST be a better way to do this.
Hypercube
Yes, Eclipse suggested that..i think the IDE got confused by my variables name (pBar - single lowercase char, before uppercase char, compared to comboAdress - a "regular" name). This must be a bug then in Eclipse SDK 3.5.1, Build id: M20090917-0800.
Hypercube
If you write the static method with the null assignment you get the warning for a smell: but you shouldn't interpret smells as errors! They only tell you that you should double check something. Personally, I think the case described in 6 is more prone to errors (specially concurrency errors) than the null assignment. Thus I would prefer the static method.Furthermore, if there was always a solution without compromises, the tool would be capable of refactoring the code without any human intervention.
nozebacle
Those sure were some words of wisdom :-). I will take your suggestion and apply it if the java dependency injection will seem improper to my particular case. I need to read some articles on the matter. Cheers.
Hypercube
+1  A: 

For point 3, probably the majority of developers these days would say the single-return rule is simply flat wrong, and on average leads to worse code. Others see that it a written-down rule, with historical credentials, some code that breaks it is hard to read, and so not following it is simply wrong.

You seem to agree with the first camp, but lack the confidence to tell the tool to turn off that rule.

The thing to remember is it is an easy rule to code in any checking tool, and some people do want it. So it is pretty much always implemented by them.

Whereas few (if any) enforce the more subjective 'guard; body; return calculation;' pattern that generally produces the easiest-to-read and simplest code.

So if you are looking at producing good code, rather than simply avoiding the worst code, that is one rule you probably do want to turn off.

soru
Bottom line regarding this one-return pattern is (in my opinion) this : try to have a single return point in a method, if possible, if not, add a supress warning and move on. I think this covers both aspects of readability and performance. Can u not agree with that? :-)
Hypercube
However, deleting a rule, using integrated config of PMD, does NOT makes the warning disappear, even in the case of a full rebuild :-). Please read question #1.
Hypercube
First point - not really, it's not a performance issue, its minimal readability versus optimal readability. A lot of the time the single-exit will be marginally faster, that doesn't make it better if it ends up more convoluted. Follow the rule or drop it, supressing it case-by-case is a waste of effort.Second point, if this is Eclipse, when checking tools aren't fully integrated, you often have to select some special 'delete markers' option to get rid of them. Or even select them in the errors window and manually delete them.
soru