Pass data through call stack without method modification - java

I've got some crazy task, that sounds like mission impossible. I need to pass some data through stack of methods, which I can't modify (can modify only the last one). Example:
SomeData someData; //not passed in method1
obj1.method1(...);
here is obj1 class code
obj1 {
someReturnClass method1(...) {
...
obj2.method2(...);
...
}
}
obj2 and method2 call some more methods, before they get to objN.methodM(). It can even be run in separate thread (so, ThreadLocal won't help). I need to access someData inside methodM, which is not passed through this stack as parameter.
I've got some concepts to get it through exception and double running methodM, but it looks ugly.
Do you have any ideas, how to pass someData to methodM()?

If you can't smuggle access any other way -- e.g. by adding a reference to SomeData into some other object that is passed through the call stack -- then you will eventually have to use a global variable. This is of course a poor design, but nothing else is possible given your constraints.
You mentioned in a comment that you may have several calls to your method "active" (is it recursive, or do you have multiple threads?) In that case, you will need to have a global collection instead, and have some way of inferring which element of the collection to select from the data that is passed through the call stack.

I understand that you need to access a local variable inside a method activation, of a method which you can't change, but which you know exists lower on the stack.
The obvious thing here is to work with the Java Debugging Architecture: http://docs.oracle.com/javase/7/docs/technotes/guides/jpda/index.html
This will allow you to examine the stacks of all threads.

Finally, I found solution:
Create JAAS Subject
Subject subject = new Subject();
Put data somewhere in subject's principals or credentials:
subject.getPublicCredentials().add(new String("Trololo"));
Get this subject and it's data anywhere you need (works even in another thread):
Subject subject = Subject.getSubject(AccessController.getContext());
System.out.println(subject.getPublicCredentials());
It won't work only in one case: thread started before subject was created.

Related

Weird negative length array created into Class.getDeclaredFields that makes JVM to throw a NegativeArraySizeException

I Use Java 15 openjdk and tried on Java 14
Include details about your goal
I'm making a RMI system in order to make instances of any object synchronisable between computers and make multiple engines works on the same object. With my system, when i want to synchronise an object, i generate a class that will extends the object class and then override every methods of the class in order to control if the method call must be delegated to the object or to perform a RMI request instead.
The class generation is divided in two part :
I generate the source code in which every non-final methods are overriden in order to add my delegating system. the code is generated in scala language, and this class yet does not extends from the class of the object to synchronise because scala don't let me override some methods, even if they are not final (it's a thing with scala's setters and getters), then i compile the code using the Scala Compiler.
I use javassist to modify the generated class and make it extends the expected class + i add some methods and modify anonfun methods in order to perform super calls.
What is happening when i see the exception ?
I have a module Server and a module Client, they both run the same code except that they have different implementation of the Engine module, which is where i define all features of my framework, my RMI system is a feature of the framework for example, and for this RMI system, absolutely no code is runned into the implementations modules.
In the Engine module, I've made a player command in my program that creates a synchronised list (of type scala.collections.mutable.ListBuffer), thus, with this command i can add some player objects to the list. for example, if i add a player to the list, it will be added in the local list of the program that executes the command, and a RMI request will be done to the other computers that hosts the list in order to make them add the same object in their list.
Now, if i enter something like player add id=7 name=testPlayer x=78 y=23, it will start to get completely weird :
First of all, this exception occurs only when the server program handles the RMI request, which is completely nonsensical because as i said, nothing is run in the implementation.
For example, if i enter the command on the server, the player will be added in it's local list, and a RMI request for the add method will be sent from the server to the client, but on the client, as it will handle the request, will not crash at all (i can spam the command, nothing breaks). So, if the server handles the RMI request, it throws me this error :
java.lang.NegativeArraySizeException: -531627648
at java.base/java.lang.Class.copyFields(Class.java:3538)
at java.base/java.lang.Class.getDeclaredFields(Class.java:2341)
at fr.linkit.engine.connection.packet.serialization.tree.ClassDescription.listAllSerialFields$1(ClassDescription.scala:45)
at fr.linkit.engine.connection.packet.serialization.tree.ClassDescription.listSerializableFields(ClassDescription.scala:52)
at fr.linkit.engine.connection.packet.serialization.tree.ClassDescription.<init>(ClassDescription.scala:23)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultClassProfile.<init>(DefaultClassProfile.scala:23)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultSerialContext.$anonfun$getClassProfile$1(DefaultSerialContext.scala:61)
at scala.collection.mutable.HashMap.getOrElseUpdate(HashMap.scala:454)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultSerialContext.getClassProfile(DefaultSerialContext.scala:61)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.getClassProfile(DefaultNodeFinder.scala:49)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.getSerialNodeForType(DefaultNodeFinder.scala:36)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.getSerialNodeForRef(DefaultNodeFinder.scala:44)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.$anonfun$listNodes$1(DefaultNodeFinder.scala:55)
at scala.collection.immutable.List.map(List.scala:246)
at fr.linkit.engine.connection.packet.serialization.tree.DefaultNodeFinder.listNodes(DefaultNodeFinder.scala:53)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ObjectNode$ObjectSerialNode.serialize(ObjectNode.scala:58)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.serializeItem$1(ArrayNode.scala:84)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.$anonfun$serialize$1(ArrayNode.scala:68)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.$anonfun$serialize$1$adapted(ArrayNode.scala:67)
at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.serialize(ArrayNode.scala:67)
at fr.linkit.engine.connection.packet.serialization.tree.LengthSign$.$anonfun$of$2(LengthSign.scala:62)
at fr.linkit.engine.connection.packet.serialization.tree.LengthSign$.$anonfun$of$2$adapted(LengthSign.scala:54)
at scala.collection.immutable.List.foreach(List.scala:333)
at fr.linkit.engine.connection.packet.serialization.tree.LengthSign$.of(LengthSign.scala:54)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ObjectNode$ObjectSerialNode.serialize(ObjectNode.scala:63)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.serializeItem$1(ArrayNode.scala:84)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.$anonfun$serialize$1(ArrayNode.scala:68)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.$anonfun$serialize$1$adapted(ArrayNode.scala:67)
at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1323)
at fr.linkit.engine.connection.packet.serialization.tree.nodes.ArrayNode$ArraySerialNode.serialize(ArrayNode.scala:67)
at fr.linkit.engine.connection.packet.serialization.DefaultSerializer.serialize(DefaultSerializer.scala:34)
at fr.linkit.engine.connection.packet.serialization.SimpleTransferInfo.makeSerial(SimpleTransferInfo.scala:38)
at fr.linkit.engine.connection.packet.serialization.LazyPacketSerializationResult.bytes$lzycompute(LazyPacketSerializationResult.scala:27)
at fr.linkit.engine.connection.packet.serialization.LazyPacketSerializationResult.bytes(LazyPacketSerializationResult.scala:27)
at fr.linkit.engine.connection.packet.serialization.LazyPacketSerializationResult.writableBytes$lzycompute(LazyPacketSerializationResult.scala:30)
at fr.linkit.engine.connection.packet.serialization.LazyPacketSerializationResult.writableBytes(LazyPacketSerializationResult.scala:29)
at fr.linkit.server.connection.ExternalConnectionSession.send(ExternalConnectionSession.scala:53)
at fr.linkit.server.connection.ServerExternalConnection.$anonfun$sendPacket$1(ServerExternalConnection.scala:100)
at fr.linkit.engine.local.concurrency.pool.BusyWorkerPool.$anonfun$runLater$1(BusyWorkerPool.scala:351)
at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.scala:18)
at scala.util.Try$.apply(Try.scala:210)
at fr.linkit.engine.local.concurrency.pool.BusyWorkerPool.$anonfun$runLaterControl$1(BusyWorkerPool.scala:122)
at fr.linkit.engine.local.concurrency.SimpleAsyncTask.runTask(SimpleAsyncTask.scala:75)
at fr.linkit.engine.local.concurrency.pool.BusyWorkerThread.runTask(BusyWorkerThread.scala:67)
at fr.linkit.engine.local.concurrency.pool.BusyWorkerPool.$anonfun$runLaterControl$2(BusyWorkerPool.scala:132)
The exception occurs during the serialization of the response packet, (as we are performing a Remote Method Invocation, we have to send the return value of the method). In this case, the add method returns the instance of the list, so the packet will contains the instance of the list as a result value (sounds useless but i have to deal with this kind of situations). When the list gets serialized, it crashes here :
def listAllSerialFields(cl: Class[_]): Seq[Field] = {
if (cl == null)
return Seq.empty
val fields = cl.getDeclaredFields //Line 45, Here, the cl value is the generated class
fields
.filterNot(p => Modifier.isTransient(p.getModifiers) || Modifier.isStatic(p.getModifiers))
.tapEach(_.setAccessible(true))
.toList ++ listAllSerialFields(cl.getSuperclass)
}
Then, further in the method it crashes here :
private static Field[] copyFields(Field[] arg) {
Field[] out = new Field[arg.length]; //arg.length is -500 millions !
ReflectionFactory fact = getReflectionFactory();
for (int i = 0; i < arg.length; i++) {
out[i] = fact.copyField(arg[i]);
}
return out;
}
I suspect that it's the reflection data that causes that because, when i used the debugger to follow the thread execution, the jvm crashed when the debugger saw the referent field of the SoftReference<ReflectionData> Class.reflectionData field. But i repeat, on the client it does not crash, and my debugger can inspect the reflection data successfully.
EDIT -
If i call getDeclaredFields directly once the class get loaded (here for example):
var loader = puppetClass.getClassLoader
if (loader == null)
loader = getClass.getClassLoader //Use the Application's classloader
val classLoader = new GeneratedClassLoader(folderPath, loader, Seq(classOf[LinkitApplication].getClassLoader))
val clazz = Class.forName(wrapperClassName, false, classLoader).asInstanceOf[Class[_ <: PuppetWrapper[AnyRef]]]
println(s"CREATED CLASS ${clazz} IN THREAD = " + Thread.currentThread())
clazz.getDeclaredFields //Invoking a method in order to make the class load its reflectionData (causes fatal error if not made directly)
ClassMappings.putClass(clazz)
clazz
It will never crash or throw me the an exception, however, it still weird that i have to do that because only the server would oftenly crash, and when it crashes, it can does it in the exact same thread that have loaded the class...
This isn't really an answer to your original question, but re-iterates what was said in the comments. What you're doing is "unsafe", and the behavior is undefined. The fact that the workaround is working at all is somewhat incidental and cannot be relied upon. Maybe it works now, but in a future Java version it might fail.
The Unsafe class is going away once safe replacements exist for all of its useful capabilities. This will likely occur after the completion of the Panama project, which provides access to native memory. The VarHandle class is the replacement for direct field access, but it doesn't permit modifying final fields, and it likely never will. Such a backdoor prevents certain optimizations, and new Java features like "records" and "hidden classes" trust that final fields are really final. This behavior might apply to all classes at some point.
There's no planned safe alternative for allocating classes without a constructor, and so that's a problem too. The built-in Java serialization mechanism will have to continue using a backdoor until it's rewritten to use a different technique.
The safe technique is to generate a hidden constructor which performs deserialization and sets the final fields. It might also need dummy parameters to avoid conflicts with any other constructors. The constructor is added with an instrumentation agent which modifies serializable classes as they're loaded.
Ideally the hidden constructor should be private, but then accessing it becomes tricky. The agent should also define (or augment) a static class intializer which looks up the MethodHandle for the hidden constructor and passes it to some serialization framework layer. The private constructor will still be visible by any code which calls getDeclaredConstructors, but that's a relatively minor problem.
As for serializing the fields out in the first place, a VarHandle for each field can be passed along from the static initializer, or a private method is added which does the serialization. I think that the private method approach is better, and it just needs one MethodHandle for accessing it.
Thanks a lot for ones who tried to help me, and thanks to boneil for his answer because he made me become aware that Unsafe wasn't a great solution, but i still have to deal with it. However, i decided to answer because i just made a "discovery" with the Unsafe.allocateInstance method : When Unsafe allocates an instance, every fields of the objects are null but still neverless non detected as null by the JVM. I just had a case where i didn't knew why i got an exception in the init method of one of my allocated instances, i first started to think that it was called twice, but it turned out that i could'nt even debug the method execution with a breakpoint because it was making my JVM crash as soon as the debugger stops in the instance's method. Thus, as i could not use the debugger, i decided to use the plain old nooby printf statement, but still unable to debug as i got a NPE here :
(in java.lang.String.java)
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString(); //obj is null.
}
as you can see, the obj was null as it threw a NPE, but it still has been considerated as a normal instance. Therefore, by seeing this, i decided to use reflection to set the field that caused this issue as null (field.set(instance, null)), and i was able to print the entire object, and i was able to use the debugger ! So this means that it's an Unsafe thing that it does not even set fields' value to null, which is very anoying but, it's ok...
Now i think that my jvm crashed because as the debugger tried to introspect my allocated object, it received an NPE, even if it certainly check if the object was not null, thus, this made an internal error in the debugger, and then the JVM crashed.
EDIT: it seems like it's not just when memory is not initialised by Unsafe, "cursed null" fields looks to appear when Unsafe touches an object (putInt, putObject...). If an Unsafe method is invoked to put something in an object's memory, any null field of the object have a (high) chance to become a weird null field

Is it bad practice in Java to modify input object in void method?

In Java, assume you have a data object object with an attribute bar that you need to set with a value that is returned from a complex operation done in an external source. Assume you have a method sendRequestToExternalSource that send a request based on 'object' to the external source and gets an object back holding (among other things) the needed value.
Which one of these ways to set the value is the better practice?
void main(MyObject object) {
bar = sendRequestToExternalSource(object);
object.setBar(bar);
}
String sendRequestToExternalSource(MyObject object) {
// Send request to external source
Object response = postToExternalSource(object);
//Do some validation and logic based on response
...
//Return only the attribute we are interested in
return response.getBar();
}
or
void main(MyObject object) {
sendRequestToExternalSourceAndUpdateObject(object);
}
void sendRequestToExternalSourceAndUpdateObject(MyObject object) {
// Send request to external source
Object response = postToExternalSource(object);
//Do some validation and logic based on response
...
//Set the attribute on the input object
object.setBar(response.getBar());
}
I know they both work, but what is the best practice?
It depends on a specific scenario. Side-effects are not bad practice but there are also scenarios where a user simply won't expect them.
In any case your documentation of such a method should clearly state if you manipulate arguments. The user must be informed about that since it's his object that he passes to your method.
Note that there are various examples where side-effects intuitively are to be expected and that's also totally fine. For example Collections#sort (documentation):
List<Integer> list = ...
Collections.sort(list);
However if you write a method like intersection(Set, Set) then you would expect the result being a new Set, not for example the first one. But you can rephrase the name to intersect and use a structure like Set#intersect(Set). Then the user would expect a method with void as return type where the resulting Set is the Set the method was invoked on.
Another example would be Set#add. You would expect that the method inserts your element and not a copy of it. And that is also what it does. It would be confusing for people if it instead creates copies. They would need to call it differently then, like CloneSet or something like that.
In general I would tend to giving the advice to avoid manipulating arguments. Except if side-effects are to be expected by the user, as seen in the example. Otherwise the risk is too high that you confuse the user and thus create nasty bugs.
I would choose the first one if I have only these two choices. And the reason of that is "S" in SOLID principles, single responsibility. I think the job of doComplicatedStuff method is not setting new or enriched value of bar to MyObject instance.
Of course I don't know use case that you are trying to implement, but I suggest looking at decorator pattern to modify MyObject instance
I personally prefer the variant barService.doComplicatedStuff(object); because I avoid making copies

Does Java's pass-by-valueness mean I do not need a deepCopy function for my class?

I am having a rough time getting some of my code to work. The code starts a SwingWorker and gets the results. I am having trouble passing data to the worker and getting information back from the worker.
The data I want to pass is made up of objects of classes I defined myself. For example I have both an Item and Inventory object. The Item object contains basically all primitive types (name, price, etc) and the Inventory contains a LinkedList of Items.
I can't quite remember the series of events that lead up to my sanity check, but as a sanity check I implemented Item.getDeepCopy(Item inItem) and Inventory.getDeepCopy(Inventory) functions so that I could pass copies of Items and Inventorys to my workers. Is this needed?
How I use the deep copy functions is when I pass data to and get data from my StringWorkers. Say I launch a worker as a result of a button click. In the event handler I first get a deep copy of my classes private local copy of Inventory or Item and pass that to the workers constructor. Is this correct? Do I need to pass a deep copy? I think not..
I do this out of fear that the worker will try to modify the classes reference to the object within the worker itself causing some threading issues. But after some reading and critical thinking this couldn't be the case because java is pass-by-value, so what is passed to the worker cannot possibly lead to the GUI components data changing. Is this correct thinking?
Then when the worker is done, it calls an overridden done method I wrote. This method runs on the EDT so I can call functions from my GUI component, namely a function I call WorkerDone(boolean result, Inventory outInv). The worker calls this function and passes (not a deep copy) its local copy of Inventory or Item back to the GUI. When the GUI gets it it then performs a deep copy and sets its local Inventory or Item to this value. Is this a good use for the deep copy?
Edit: A little more.
Basically I want to pass "some data" to the worker and allow it to work on it with no link to the GUI components. When the worker is done it will either have finished successfully or not. If successful I want to get the data "back" from the worker and update my local copy with the data returned. I don't want the worker to "touch" any of the data in the GUI.
For mutable-ness. I want to be able to change the data within the object after it is created. This is how I build my application with this in mind. What I want is not non-mutable objects to keep things thread safe, I just don't want threads interacting. I want to pass the worker some data and basically "forget that I sent it" and then when the worker is done and it calls the GUI's workerDone method the GUI simply just agrees to set its local copy of the data to the value of the returned object if the worker says it was successful.
Edit 2:
Just for clearer understand of the phrases pass-by-value and pass-by-reference. What I think when I see pass-by-value. Say I want to pass an apple by value, to do this I would put my apple in a cloning machine that makes an exact clone of the apple same in every respect and pass that apple. Whomever is passed this cloned apple can do anything with it, and none of it affects my initial apple.
What I think of when I see pass-by-reference is that if I want to pass my apply by reference I write down where my apple is on a piece of paper and then pass that. Whomever receives this piece of paper can then come to where my apple is and take a bite of it.
So my confusion comes from "Java is pass-by-value", if it is, then why do I have to worry about my worker causing thread violations when operating on the value passed to it?
Java is pass by value, but when you pass an object, you are passing a reference, and just a copy of that reference. Both references, the original and copy still refer to the same set of values in the heap.
It's valid to worry about code you have no control over possibly modifying your objects, but you could perhaps wrap that object in another that cannot be changed, or specify an interface that has the extract methods but not set methods.
Having the GUI have it's own copy of data could be useful if you might update the data, and halfway through updating the data you display it. If you have no worry about such inconsistencies, you may just want to share the same reference between the gui code and non-gui code to keep your code simple (assuming your classes here are thread-safe).
"Pass by value" and "pass by reference" describe how function arguments are passed on the stack. They have nothing to do with what can or can't happen to objects on the heap. Suppose that we write code in some completely made-up programming language:
def foo(x) :
x = 5;
end
def bar() :
a = 3;
foo(a);
print a;
end
What will the function bar() print? The answer depends on whether the parameter x in foo() is pass-by-value or pass-by-reference. In a pass-by-value language (e.g., in Java), the value of the local variable a is passed when bar() calls foo(a). The foo() function can modify its own copy of that value, but it can not modify the caller's local variable, a.
In a pass-by-reference language, the address of the caller's local variable is passed. The x in foo becomes an alias for the a in bar(), and the assignment, x=5 changes the value of the variable a in bar.
Java is always pass by value: A Java function can never, ever, modify its caller's local variables.
Where people get confused is, a Java value can either be a primitive type (int, double, char, ...), or it can be an object reference.
public javaFoo(MyType x) {
x.setFrobber(true);
}
public javaBar() {
MyType mt = new MyType();
foo(mt);
System.out.println(mt.toString());
}
The variable x in javaFoo() is a different variable from the variable mt in javaBar(), and "pass-by-value" means that the javaFoo() function can not change mt. BUT both variables refer to the same object, and javaFoo() can modify the object.
"Pass by value" means that an object reference is passed. A copy of the original reference, not a copy of the object's data.
In other words, all your callees see the same object (and can change it at will).
Here's a good article from the Oracle Java docs on strategies for making an object "immutable":
http://docs.oracle.com/javase/tutorial/essential/concurrency/immutable.html

Malicious code vulnerability - May expose internal representation by returning reference to mutable object

Hi I'm getting the violation as below:
Malicious code vulnerability - May expose internal representation by
returning reference to mutable object
in my code i wrote like this
public String[] chkBox() {
return chkBox;
}
How we can solve it.
As the error message states, you're returning internal state (chkBox is - most likely - part of the internal state of an object even though you're not showing its definition)
This can cause problems if you - for example - do
String[] box = obj.chkBox();
box[0] = null;
Since an array object, as all Java objects, is passed by reference, this will change the original array stored inside your object as well.
What you most likely want to do to fix this is a simple
return (String[])chkBox.clone();
which returns a copy of the array instead of the actual array.
Let's suppose the following:
Your class does something that matters from a security or privacy perspective, and that the state of chkbox is somehow used in the classes implementation of its privacy / security mechanisms.
The chkBox() method can be called by some code that is not trusted.
Now consider this code:
// ... in an untrusted method ...
Foo foo = ...
String[] mwahaha = foo.chkBox();
mwahaha[0] = "Gotcha!"; // ... this changes the effective state of `Foo`
By returning a reference to the actual array that represents the chkbox, you are allowing code external to the Foo class to reach in and change its state.
This is bad from a design perspective (it is called a "leaky abstraction"). However, if this class is used in a context where there may also be untrusted code, this (the chkBox() method) is a potential security hole. That is what the violation message is telling you.
(Of course, the code checker has no way of knowing if this particular class is really security critical. That's for you to understand. What it is actually saying to you is "Hey! Look here! This is suspicious!")
The fix depends on whether this code (or indeed the entire library or application) is security critical ... or code be security critical in some future deployment. If this is a false alarm, you could just suppress the violation; i.e. mark it so that will be ignored by the checker. If this is a real issue (or could become a real issue), then either return a copy of the array:
return (String[]) chkBox.clone();
But clearly, there is a performance cost in cloning the array each time you call chkBox. Alternatively, you could modify the chkBox method to return a selected element of the array:
public String chkBox(int i) {
return chkBox[i];
}
In this case, I suspect that the alternative approach will be better ... though it depends on how the method is currently used.

Should I keep instance variables in Java always initialized or not?

I recently started a new project and I'm trying to keep my instance variables always initialized to some value, so none of them is at any time null. Small example below:
public class ItemManager {
ItemMaster itemMaster;
List<ItemComponentManager> components;
ItemManager() {
itemMaster = new ItemMaster();
components = new ArrayList<ItemComponentManager>();
}
...
}
The point is mainly to avoid the tedious checking for null before using an instance variable somewhere in the code. So far, it's working good and you mostly don't need the null-value as you can check also for empty string or empty list, etc. I'm not using this approach for method scoped variables as their scope is very limited and so doesn't affect other parts of the code.
This all is kind of experimental, so I'd like to know if this approach could work or if there are some pitfalls which I'm not seeing yet. Is it generally a good idea to keep instance variables initialized?
I usually treat an empty collection and a null collection as two separate things:
An empty collection implies that I know there are zero items available. A null collection will tell me that I don't know the state of the collection, which is a different thing.
So I really do not think it's an either/or. And I would declare the variable final if I initialize them in the constructor. If you declare it final it becomes very clear to the reader that this collection cannot be null.
First and foremost, all non-final instance variables must be declared private if you want to retain control!
Consider lazy instantiation as well -- this also avoids "bad state" but only initializes upon use:
class Foo {
private List<X> stuff;
public void add(X x) {
if (stuff == null)
stuff = new ArrayList<X>();
stuff.add(x);
}
public List<X> getStuff() {
if (stuff == null)
return Collections.emptyList();
return Collections.unmodifiableList(stuff);
}
}
(Note the use of Collections.unmodifiableList -- unless you really want a caller to be able to add/remove from your list, you should make it immutable)
Think about how many instances of the object in question will be created. If there are many, and you always create the lists (and might end up with many empty lists), you could be creating many more objects than you need.
Other than that, it's really a matter of taste and if you can have meaningful values when you construct.
If you're working with a DI/IOC, you want the framework to do the work for you (though you could do it through constructor injection; I prefer setters)
-- Scott
I would say that is totally fine - just as long as you remember that you have "empty" placeholder values there and not real data.
Keeping them null has the advantage of forcing you to deal with them - otherwise the program crashes. If you create empty objects, but forget them you get undefined results.
And just to comment on the defencive coding - If you are the one creating the objects and are never setting them null, there is no need to check for null every time. If for some reason you get null value, then you know something has gone catastrophically wrong and the program should crash anyway.
I would make them final if possible. Then they have to be initialized in the constructor and cannot become null.
You should also make them private in any case, to prevent other classes from assigning null to them. If you can check that null is never assigned in your class then the approach will work.
I have come across some cases where this causes problems.
During deserialization, some frameworks will not call the constructor, I don't know how or why they choose to do this but it happens. This can result in your values being null. I have also come across the case where the constructor is called but for some reason member variables are not initialized.
In actual fact I'd use the following code instead of yours:
public class ItemManager {
ItemMaster itemMaster = new ItemMaster();
List<ItemComponentManager> components = new ArrayList<ItemComponentManager>();
ItemManager() {
...
}
...
}
The way I deal with any variable I declare is to decide if it will change over the lifetime of the object (or class if it is static). If the answer is "no" then I make it final.
Making it final forces you to give it a value when the object is created... personally I would do the following unless I knew that I would be changing what the point at:
private final ItemMaster itemMaster;
private final List components;
// instance initialization block - happens at construction time
{
itemMaster = new ItemMaster();
components = new ArrayList();
}
The way your code is right now you must check for null all the time because you didn't mark the variables as private (which means that any class in the same package can change the values to null).
Yes, it is very good idea to initialize all class variables in the constructor.
The point is mainly to avoid the
tedious checking for null before using
a class variable somewhere in the
code.
You still have to check for null. Third party libraries and even the Java API will sometimes return null.
Also, instantiating an object that may never be used is wasteful, but that would depend on the design of your class.
An object should be 100% ready for use after it's constructed. Users should not have to be checking for nulls. Defensive programming is the way to go - keep the checks.
In the interest of DRY, you can put the checks in the setters and simply have the constructor call them. That way you don't code the checks twice.
If it's all your code and you want to set that convention, it should be a nice thing to have. I agree with Paul's comment, though, that nothing prevents some errant code from accidentally setting one of your class variables to null. As a general rule, I always check for null. Yeah, it's a PITA, but defensive coding can be a good thing.
From the name of the class "ItemManager", ItemManager sounds like a singleton in some app. If so you should investigate and really, really, know Dependency Injection. Use something like Spring ( http://www.springsource.org/ ) to create and inject the list of ItemComponentManagers into ItemManager.
Without DI, Initialization by hand in serious apps is a nightmare to debug and connecting up various "manager" classes to make narrow tests is hell.
Use DI always (even when constructing tests). For data objects, create a get() method that creates the list if it doesn't exist. However if the object is complex, almost certainly will find your life better using the Factory or Builder pattern and have the F/B set the member variables as needed.
What happens if in one of your methods you set
itemMaster = null;
or you return a reference to the ItemManager to some other class and it sets itemMaster as null.
(You can guard against this easily return a clone of your ItemManager etc)
I would keep the checks as this is possible.

Categories