'Null pointer access' when using java output parameters
I am working in java and wanting to use something like output parameters. Here is an example:
ClassA objA = null;
if(myMethod(objA))
{
// position (1)
//use objA somehow
}
======================
public bool myMethod(ClassA obj)
{
obj = .....
}
I am using Eclipse and the problem I'm having is that eclipse shows the warning:
Null pointer access. The variable objA can only be null at this location
when i reach position (1)
Since there is no concept of out
parame开发者_如何学Cters in java i'm a little stumpped
==================================================================
EDIT: I have gotten several people mention to change the method to return my object instead of bool. But what If i need a method to alter several objects? ex: ClassA objA = null;
ClassB objB = null;
if(myMethod(objA, objB))
{
// position (1)
//use objA and objB somehow
}
======================
public bool myMethod(ClassA obj, ClassB obj2)
{
//do stuff
}
How can I do this? I dont want to make a custom class for every flavor of return objects possible?
thanks,
StephanieSince there is no concept of out parameters in java ...
The reference obj
is a copy of the reference objA
, so assigning a new object to obj
will not change objA
. This means that using obj
as an out parameter is not supported by java.
Because of this a java method can return only one value. The following are possible solutions/workarounds.
Solution 1: exception instead of boolean (only if boolean indicates an error)
ClassA objA = null;
try{
objA = myMethod();
//DO something with objA
}catch(MyException ex){
}
Solution 2: return null in case of an error.
ClassA objA = null;
objA = myMethod();
if(objA != null)
{ //DO something with objA
}
Solution 3: use a Pair to return several values
MyPair mp = myMethod();
if(mp.first){
}
MyPair myMethod(){
MyPair ret = new MyPair();
mp.first = ...;//boolean
mp.second = new ClassA();
return ret;
}
class MyPair {
boolean first;
ClassA second;
}
Soultion 4: use single element arrays - ugly only use in extreme cases
ClassA[] objA = new ClassA[1];
if(myMethod(objA))
{
}
boolean myMethod(ClassA[] obj){
obj[0] = new ClassA();
}
You can't really do what you're trying to do in java since, as you say, java doesn't support output parameters.
Method parameters are local variables, so making assignments to them has no effect outside the scope of the method. In other words, this:
public void foo(Object obj) {
obj = new Object();
}
Is effectively equivalent (from the point of view of the code calling foo(Object)) as this:
public void foo() {
Object obj = new Object();
}
Pointless, since the created object will be thrown out once foo()
returns.
Probably, what you want to do is change your method to return the object your method creates:
public ClassA myMethod() {
ClassA obj = ....
...
return obj;
}
Then in your calling code:
ClassA objA = myMethod();
if (objA != null) {
...
}
Alternatively, you could instantiate the instance of ClassA outside your method and pass that value is, and have the method modify it in some way:
public boolean myMethod(ClassA obj) {
obj.setValue(...);
return true;
}
...
ClassA objA = new ClassA();
if (myMethod(objA) {
Object val = objA.getValue();
...
}
Without knowing more about your specific problem, it's hard to say which design is better.
UPDATE:
The multi-parameter example you added is flat-out impossible in java, unfortunately. Everyone says java is pass-by-reference, but in reality it's more like pass-by-reference-value. A method can modify objects passed to it, but it cannot modify what the variables in the calling scope refer to. If you come from a C++ background, object references in java are more like pointers (without the pointer arithmetic) than they are like C++ references.
To make this more concrete, consider the following class:
public class ParameterPassing {
public static void setParams(Integer value1, Integer value2) {
System.out.println("value1 before: " + value1);
System.out.println("value2 before: " + value2);
value1 = new Integer(1);
value2 = new Integer(2);
System.out.println("value1 after: " + value1);
System.out.println("value2 after: " + value2);
}
public static void main(String[] args) {
Integer valNull = null;
Integer val0 = new Integer(0);
System.out.println("valNull before: " + valNull);
System.out.println("val0 before: " + val0);
setParams(valNull, val0);
System.out.println("valNull after: " + valNull);
System.out.println("val0 after: " + val0);
}
}
When you run this, you'll get this output:
valNull before: null
val0 before: 0
value1 before: null
value2 before: 0
value1 after: 1
value2 after: 2
valNull after: null
val0 after: 0
As you can see, the assignments inside the setParams()
method have no effect on what valNull
and val0
refer to.
If you really need multiple "output" parameters for a single method, you're going to have to wrap them in some other object, or rethink your design. Perhaps you could make the references member variables rather than locals and have your method modify them directly:
public class MyClass {
private ClassA objA;
private ClassB objB;
...
private boolean initObjects() {
objA = ...;
objB = ...;
return true;
}
public void otherMethod() {
...
if(initObjects() {
// Use objA, objB
}
}
}
Java is pass by value. When you assign a value to obj
in myMethod, you assign a new value to a copy of the pointer of the calling code. The calling code's pointer continues pointing to its initial object, which is null. This is why Eclipse warns you : you're trying to use objA, which is null, whatever you do in myMethod.
Assigning a value to obj
within myMethod
will not change the value of objA
. In Java, references are passed by value. If you want myMethod
to initialize an instance of ClassA
, you should move that logic either into a constructor of ClassA
or a factory method.
To signal an error condition, you would throw an exception instead of returning a boolean value.
I'm pretty sure that behavior is not supported in Java; i.e you can't do that. Return your object or null instead of a bool.
Basically, you can't do that with Java. When you call a method, you are passing a reference to the object, you are not passing a memory location that a method can write to. What you can do is pass in an object and allow the function to manipulate the object. Then when the function returns, you can access the fields that were manipulated but the function.
ClassA objA = new ClassA();
if(myMethod(objA))
{
objA.getValue();
}
public boolean myMethod(ClassA obj)
{
obj.assignValue(1);
return true;
}
Actually, Eclipse is right in this case. Consider the following code:
public class NullThing {
private static class ClassA {
public int x;
}
public static boolean myMethod(ClassA obj) {
obj = new ClassA();
obj.x = 5;
return obj.x == 5;
}
public static void main(String[] args) {
ClassA objA = null;
if (myMethod(objA)) {
System.out.println(objA.x);
}
}
}
If you compile this with javac
to avoid Eclipse's warning, you'll end up with:
adrian:~/ $ javac NullThing.java [14:05:47]
adrian:~/ $ java NullThing [14:06:43]
Exception in thread "main" java.lang.NullPointerException
at NullThing.main(NullThing.java:15)
Read about Java's semantics in this area in this section of the Java Language Specification, but it's probably pretty obvious from the code snippet what's actually going on.
精彩评论