tags:

views:

876

answers:

10

The use case is this:

public void testMethod(String para1, String para2, String para3){


 if(para1==null){
         System.out.println("para1 cannot be null");

   }
 if(para2)...
}

As the check null code above, we will be repeating ourselvous on writing the same code to check every parameter. But we cannot really factor out a common method, say, checknull(String para), because we need to output the name of the parameter so the users know which one is wrong.

Maybe there is no way to do this in java I guess. Method parameter names should be gone after compile if I understand it correctly.

So, how do you guys usually address this problem?

+4  A: 

It's put in the message. No other way to do it. And no, you can't get the variable name.

I suggest you consider using Java's assert feature, which is highly-underused. It can be quite concise too:

public void testMethod(String para1, String para2, String para3) {
  assert para1 != null : "para1 is null";
  assert para2 != null : "para2 is null";
  assert para3 != null : "para3 is null";
}

You just need to enable assertions in the VM with the -ea parameter. That's another advantage: you can turn them on or off as a runtime option.

It should be noted that the above will generate an AssertionError, which is an Error (in the Java sense), so won't be caught by a catch (Exception e) block. So they should be used for conditions that really aren't recoverable or things that should never happen.

Generally if a user breaks the contract (by passing in a null when they shouldn't, for example) then an appropriate RuntimeException may be better.

cletus
With this approach you should assure that the parameter checking is not part of needed logic of your application (I think you don't want your application works in a different way according a JVM parameter)
diega
the null check should always be in there (in my opinion). As asserts can be disabled, this most likely means that they will BE disabled when you really need them - in a rare production scenario.
Thorbjørn Ravn Andersen
not recomended by SUN: "Do not use assertions for argument checking in public methods." http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html
Carlos Heuberger
A: 

What I do is similar to what you have, except I throw a NullPointerException (per Effective Java, personally, I'd have preferred IllegalArgumentException).

...
if (para1 == null) {
    throw new NullPointerException("para1 cannot be null");
}
...

If you want to refactor it out to a method, you could do something along the lines of:

assertNotNull(Object param, String paramName) {...}
Jack Leow
Thats pretty much what I did in the end. I reckon with normal java we couldnt do better than that, couldnt we.
I dislike NullPointerExceptions - they should in my opinion only be thrown by the JVM, not by user code. IllegalArgumetnException("para1 == null") is what I do.
Thorbjørn Ravn Andersen
A: 

Why don't you pass them along as a map? This way you could have a key representing the name of the variable and a value representing the actual String variable.

Geo
wouldn't that mean MORE work?
Thorbjørn Ravn Andersen
A: 

Although it may be overkill, I guess it is also worth mentioning that you could check your arguments using a 3rd party design by contract framework. The Java implementations seem to rely on doclet comments or annotations to specify the rules. I've only taken a cursory look at them, so I can't recommend any one in particular.

McDowell
+1  A: 

I believe that Java 7 will have standard annotations requiring that a given parameter cannot be null. Unfortunately I have not seen these annotation for Java 6 or earlier :(

Thorbjørn Ravn Andersen
+1  A: 

Use the varargs notation in Java 1.5 . Declare your arguments as "Object... objects" and loop thru the collection.

public static void printSpaced(Object... objects) { for (Object o : objects) { System.out.print(o + ":"); if ( o == "x" ) { System.out.print ("x found"); } } }

blispr
"But we cannot really factor out a common method, say, checknull(String para), because we need to output the name of the parameter so the users know which one is wrong."
Cambium
A: 

Hmm, even if you could dynamically get the parameter names, I don't see that there would be much benefit. It's not that tough to write:

checkNull("parm1", parm1);
checkNull("parm2", parm2);
checkNull("parm3", parm3);

By the way, I hope this is just example code, and you don't REALLY call your parameters parm1, parm2, and parm3, but something more meaningful like customerName, balanceDue, and accountType.

And once you say that, it should quickly become apparent that in a real application, the validation requirements for each parameter will likely be different. customerName should not be null, but it probably also should not be an empty string. balanceDue should contain only digits and at most one decimal point and possibly a leading minus sign. Etc. So you can't just loop through all your parameters performing the same validation on each, rather you must do the appropriate validation for each.

Unless you have dozens of parameters, the code to write a loop would be almost as much trouble as just writing several calls to a common validation function anyway. And if you have dozens of parameters, this is usually a bad thing of itself.

Jay
A: 

I haven't used it, but Paranamer can give you runtime access to parameter names (at the cost of requiring debug info in code and using reflection to get parameter names). This is almost certainly overkill for your problem however.

Also note that the FindBugs and JSR305 annotations (the latter should make it to Java7, but can be used now with FindBugs and downloaded from the FindBugs site or Maven distribution) can be used in Java5 and above, to provide some level of compile-time checking of parameter nullness. I think that this is nice, and provides at least good documentation (as well as catching some errors), but is definitely not the same as a runtime check (which should catch all such errors, albeit only at runtime), and so as such is not a replacement (though perhaps you could write an AspectJ aspect that validated the parameters to methods with such annotations).

paulcm
+1  A: 

If you want to check the value of a variable at run time, you can use reflection to check the instantiated objects fields like this:

package sandbox;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class ReflectionClassChecker {

    public static boolean checkAllPublic(Object someObject){
     System.out.println("Checking someObject " + someObject.toString());
     boolean hasNulls = false;

     Class<?> c = someObject.getClass();

     Field[] fields = c.getFields();

     for(Field field: fields){
      System.out.println("Checking field " + field.getName() + ".");
      if(isFieldPublic(field)){
       System.out.println("Field " + field.getName() + " is public, checking it for null.");
       Object value = getField(field, someObject) ; 
       if(value == null){
        System.out.println("Field " + field.getName() + " is null.");
        hasNulls = true;
       } else {
        System.out.println("Field " + field + " has value " + value );
       }
      }
     }

     return hasNulls;
    }

    private static boolean isFieldPublic(Field field){
     int modifiers = field.getModifiers();
     boolean isPublic = Modifier.isPublic(modifiers);  
     return isPublic;
    }

    private static Object getField(Field field, Object someObject){
     Object value = null;
     try{
      value = field.get(someObject);
     } catch (IllegalAccessException ignore){
      System.out.println(ignore);
     }  
     return value;
    }
}

You can easily go from this implementation to one which uses getter/setter method invocations to check the value of inaccessible fields. If you want something less generic, or you want something which only checks specific fields, you can use c.getField(String) to get just that field, and then call field.get(object) for just that field.

hasalottajava
A: 

Use an annotation on the param and add a dynamic proxy (either Java 4 style of CGLIB) to do the check See link text on how to use CGLIB

rparree