So I'm (remotely) debugging a java/jboss application in Eclipse, stepping through line by line. At one point, an array of GridSquare objects (GridSquare is a fairly simple, standalone class, contains a few properties and methods) is created by a method call, i.e:
GridSquare[] squares = this.theGrid.getSquares(14, 18, 220, 222);
...While when I actually execute the code, the squares array does get populated with GridSquare objects, I get something odd when stepping through the code and debugging. At a breakpoint on the line immediately following the assignment shown above, if I try to view the squares array, instead of a value I get this:
org.eclipse.debug.core.DebugException: com.sun.jdi.ClassNotLoadedException: Type has not been loaded occurred while retrieving component type of array.
...Anyone know what that's about?
Basically it means the class loader has not loaded the GridSquare[] class. That being said it sounds like a bug in the debugger in some way. The breakpoint association with the code seems to be slightly broken. Either you need to recompile to get the line numbers in sync or some other issue is going on. At that point in the code (after the assignment) it needs to be loaded. Unless getSquares actually returns a subclass (GridSquareSubclass[]) at which point the JVM may not have loaded it because it doesn't need it (yet).
I ran into this error because I was running a unit test on code that uses reflection. I had made a special class for testing all the features of the code. Junit apparently uses a separate classloader for test classes (which makes perfect sense) so my code was not able to use reflection on that class. The stack trace I got was extremely generic (java.lang.InstantiationException) but when I checked in debug mode there was more detail on the Exception object itself (org.eclipse.debug.core.DebugException: com.sun.jdi.ClassNotLoadedException) which led me to this conclusion.
So I moved the special class to the main classloader (by moving the file from src/test/java to src/main/java) and it worked fine. I don't like this solution but I cannot figure out an alternative. In my case it's not a big deal but I can see how this might be a concern for others.
I have seen this happen in Eclipse when you have a subclass's class variables hiding a parent class's variables. Somehow that confuses Eclipse (and generally is a bad idea anyway :). For example:
class A {
private String a;
}
class B extends A {
public String a;
}
//Give a SIZE to the array:
GridSquare[] squares = GridSquare[this.theGrid.size()];
//Fill each element of the array with the object constructor to avoid the null value
for(int i=0; i<this.theGrid.size(); i++){
squares[i] = new GridSquare();
squares[i] = this.theGrid.getSquares(14, 18, 220, 222);
}
By initializing GridSquare will solve the problem.
squares =new GridSquare();
I faced the same problem, i just created a public static void main method, created object of same type and Run As java Application, i then removed main method, it now works fine.
Related
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
I am creating a set of TestNG tests in eclipse for existing code that supposedly validates certificates against a CRL.
In my test, I create a new class to provide validation functions, like this:
public void testRevokedCertificate() throws Exception {
EmbeddedFileServer embeddedFileServer = new EmbeddedFileServer(CertificateResourceHelper.getResourcePath("."));
embeddedFileServer.start();
URL crlUrl = new URL("http://localhost:" + embeddedFileServer.getPort() + "/certs/" + "test_ca1.crl");
CachingValidCrlProvider prov = new CachingValidCrlProvider(crlUrl, publicKey, 1, 2);
assertNotNull(prov);
}
The constructor CachingValidCrlProvider(URL, publicKey, int, int) always returns null! This is a mystery to me, as the constructor does not look very special:
public CachingValidCrlProvider(URL crlUrl, PublicKey expectedPublicKey, int failedDownloadBackoffTimeInSeconds, int forcedCrlRefreshIntervalInSeconds) {
System.out.println("Creating CachingValidCrlProvider (this is never displayed)");
this.crlUrl = crlUrl;
this.expectedPublicKey = expectedPublicKey;
crlDownloadState = new CrlDownloadState(failedDownloadBackoffTimeInSeconds, forcedCrlRefreshIntervalInSeconds);
}
The first debug println() is somehow never reached since I cannot find the printed line in stdout.
AFAIK, one of the few ways constructors may yield null is when a static {} block fails somewhere down the line. However, I do not see any (there is one such block in the codebase but not reachable in this scenario).
How do I troubleshoot this issue?
Quote from an answer here on StackOverflow by Jon Skeet:
From section 15.9.4 of the JLS:
The value of a class instance creation expression is a reference to the newly created object of the specified class. Every time the expression is evaluated, a fresh object is created.
So no, it can never return null.
A constructor in Java CANNOT return null. The only case your object can be null is if you didn't call the constructor, or the constructor raised an exception.
EDIT:
As for your code, the most probable possibility IMO is that the code doesn't stop on your assertNotNull(prov), but never reaches it and instead crashes somewhere before. You should try using the debugger/more sysout.
Static Initialization Blocks are executed before anything else, So the code is reached, you just don't know it. I suggest you add a breakpoint in your static block, and use a debugger (included in eclipse or Intellij) to see when it fails. good luck.
Thank you all for the responses. They helped me get to the root cause. As it turned out, there was a mock initialization of this class in another test (there are ~300 existing tests by other people in this codebase).
Because the code uses singletons extensively, my call to new did not actually change anything to the already-initialized object.
I found this thanks to your remarks and hints which made me look in different parts of the codebase, thanks a lot.
I'm busy making a Java game, and for enemies, I store them in an ArrayList. But when I try to use the get(int) method, it keeps giving me The type of get(int) is erroneous where E is a type-variable: Why is this? Shouldn't it just return the element? I've searched a while and haven't been able to find a solution to this.
Here's the part of my code:
for(int i=0;i<holes.size();i++){
hole temp = holes.get(i);
if((p1.playerRec.x/48)==(temp.holeRec.x/48) && (p1.playerRec.y/48)==(temp.holeRec.y/48)){
gameOver=true;
}
}
public void findHoles(){
for(int i=0;i<map1.height;i++){
for(int j=0;j<map1.width;j++){
if(map1.tileMap[i][j]==5){
addHole(new hole(i,j));
}
}
}
}
public void addHole(hole h){
holes.add(h);
}
Hey generic user2305801,
as much as I have to emphasise using syntax conventions, as Stephen C is insisting on, I'm sure you're code is not causing this specific problem, when you declare class-names lowercase.
I just shot myself an arrow in the knee, by supposedly making the same mistake as you. Although you surely solved the problem in the meantime, I'll mention the possible solution for the records here:
Maybe you're missing a package statement at the top of your file in the external classfile containing the declaration for the hole-class. If the classfile already contains a package declaration then make it the same namespace as the file you're referencing the class from.
So in the referencing file write:
package whackamole;
public static void main(String[] args)
{
...
for(int i=0;i<holes.size();i++){
Hole temp = holes.get(i);
}
...
}
And in the class-file:
package whackamole;
public class Hole
{
...
}
I know the code-example is a bit superfluous, but I just wanted to try it out.
This is not making a lot of sense based in the information that you have trickle fed us ...
But there is a clue in the code.
You appear to have ignored the Java identifier naming rules and named one of your classes 'hole' instead of 'Hole'. This is bad style. But the consequences could be worse than a slap on the wrist from the style police.
The problem is that Java's name resolution rules are designed to work best when you obey the style rules. If you declare a variable/field 'hole' and you also have a class called 'hole', the JLS says that the variable / field name takes precedence ... in some contexts. This can lead to some pretty confusing compilation error messages, if you actually meant 'hole' to mean a class.
So ...
Fix the style violation by renaming the class 'hole' to be 'Hole'. This may also make the weird compilation errors go away. But you should do it anyway!
I have to work with this library that deals with a standard for biodiversity. Essentially, it reads in csv, xml etc and validates it, allowing one to iterate through the records with the type DarwinCoreRecord
For some odd reason, as soon as the object is moved from the iterator, it is gone. Small example below:
ArrayList<DarwinCoreRecord> dwc = new ArrayList<DarwinCoreRecord>();
for(; iter.hasNext();) {
DarwinCoreRecord record = iter.next();
System.out.println(record.getSomeValue());
dwc.add(record);
}
That print statement is perfect and prints out the right details, if I out that print statement outside of the for loop, nulls are had by all.
So, onto my question (unless anyone can solve the above). I created a skeleton class that extends this DarwinCoreRecord. It looks exactly like this:
public class DWCRecordStore extends DarwinCoreRecord {
public DWCRecordStore() {
}
}
This was just to test my plan would work but I am getting a classcastexception. It has been a while since I have dealt with casting (in Java, at least) but I see no issues. I am not casting from DWCRecordStore to DarwinCoreRecord so I cannot see the problem.
Does anyone see anything glaring that I have missed?
Thanks in advance.
EDIT: Sorry, error is here:
java.lang.ClassCastException: org.gbif.dwc.record.DarwinCoreRecord cannot be cast to com.encima.DWCRecordStore
at com.encima.DroolsTest.main(DroolsTest.java:37)
EDIT 2: Added my use of the casting.
DWCRecordStore drs = new DWCRecordStore();
DarwinCoreRecord dwc = iter.next();
drs = (DWCRecordStore) dwc;
You are trying to cast DarwinCoreRecord into an DWCRecordStore - but the first is not an instance of the second!
Think of DarwinCoreRecord as a polygon and DWCRecordStore as a square, you can always cast a square (DWCRecordStore) to a polygon (DarwinCoreRecord) - but not the other way around.
It might be that you are trying to store an object that was returned from the lobrary as a DWCRecordStore. But the library is unfamiliar with DWCRecordStore, it returns only DarwinCoreRecord objects, which are not DWCRecordStore.
Pinpointing the exact problem might require more code - how exactly did you use your new class.
EDIT: your last edit clarifies it, since dwc is not instance of DWCRecordStore, it cannot be casted to one.
An alternative might be creating a constructor: DWCRecordStore(DarwinCoreRecord), and using:
drs = new DWCRecordStore(dwc);
I'm in my first programming class in high school. We're doing our end of the first semester project.
This project only involves one class, but many methods. My question is about best practice with instance variables and local variables. It seems that it would be much easier for me to code using almost only instance variables. But I'm not sure if this is how I should be doing it or if I should be using local variables more (I would just have to have methods take in the values of local variables a lot more).
My reasoning for this is also because a lot of times I'll want to have a method return two or three values, but this is of course not possible. Thus it just seems easier to simply use instance variables and never having to worry since they are universal in the class.
I haven't seen anyone discuss this so I'll throw in more food for thought. The short answer/advice is don't use instance variables over local variables just because you think they are easier to return values. You are going to make working with your code very very hard if you don't use local variables and instance variables appropriately. You will produce some serious bugs that are really hard to track down. If you want to understand what I mean by serious bugs, and what that might look like read on.
Let's try and use only instance variables as you suggest to write to functions. I'll create a very simple class:
public class BadIdea {
public Enum Color { GREEN, RED, BLUE, PURPLE };
public Color[] map = new Colors[] {
Color.GREEN,
Color.GREEN,
Color.RED,
Color.BLUE,
Color.PURPLE,
Color.RED,
Color.PURPLE };
List<Integer> indexes = new ArrayList<Integer>();
public int counter = 0;
public int index = 0;
public void findColor( Color value ) {
indexes.clear();
for( index = 0; index < map.length; index++ ) {
if( map[index] == value ) {
indexes.add( index );
counter++;
}
}
}
public void findOppositeColors( Color value ) {
indexes.clear();
for( index = 0; i < index < map.length; index++ ) {
if( map[index] != value ) {
indexes.add( index );
counter++;
}
}
}
}
This is a silly program I know, but we can use it to illustrate the concept that using instance variables for things like this is a tremendously bad idea. The biggest thing you'll find is that those methods use all of the instance variables we have. And it modifies indexes, counter, and index every time they are called. The first problem you'll find is that calling those methods one after the other can modify the answers from prior runs. So for example, if you wrote the following code:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
idea.findColor( Color.GREEN ); // whoops we just lost the results from finding all Color.RED
Since findColor uses instance variables to track returned values we can only return one result at a time. Let's try and save off a reference to those results before we call it again:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes;
int redCount = idea.counter;
idea.findColor( Color.GREEN ); // this causes red positions to be lost! (i.e. idea.indexes.clear()
List<Integer> greenPositions = idea.indexes;
int greenCount = idea.counter;
In this second example we saved the red positions on the 3rd line, but same thing happened!?Why did we lose them?! Because idea.indexes was cleared instead of allocated so there can only be one answer used at a time. You have to completely finish using that result before calling it again. Once you call a method again the results are cleared and you lose everything. In order to fix this you'll have to allocate a new result each time so red and green answers are separate. So let's clone our answers to create new copies of things:
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes.clone();
int redCount = idea.counter;
idea.findColor( Color.GREEN );
List<Integer> greenPositions = idea.indexes.clone();
int greenCount = idea.counter;
Ok finally we have two separate results. The results of red and green are now separate. But, we had to know a lot about how BadIdea operated internally before the program worked didn't we? We need to remember to clone the returns every time we called it to safely make sure our results didn't get clobbered. Why is the caller forced to remember these details? Wouldn't it be easier if we didn't have to do that?
Also notice that the caller has to use local variables to remember the results so while you didn't use local variables in the methods of BadIdea the caller has to use them to remember results. So what did you really accomplish? You really just moved the problem to the caller forcing them to do more. And the work you pushed onto the caller is not an easy rule to follow because there are some many exceptions to the rule.
Now let's try doing that with two different methods. Notice how I've been "smart" and I reused those same instance variables to "save memory" and kept the code compact. ;-)
BadIdea idea = new BadIdea();
idea.findColor( Color.RED );
List<Integer> redPositions = idea.indexes;
int redCount = idea.counter;
idea.findOppositeColors( Color.RED ); // this causes red positions to be lost again!!
List<Integer> greenPositions = idea.indexes;
int greenCount = idea.counter;
Same thing happened! Damn but I was being so "smart" and saving memory and the code uses less resources!!! This is the real peril of using instance variables like this is calling methods is order dependent now. If I change the order of the method calls the results are different even though I haven't really changed the underlying state of BadIdea. I didn't change the contents of the map. Why does the program yield different results when I call the methods in different order?
idea.findColor( Color.RED )
idea.findOppositeColors( Color.RED )
Produces a different result than if I swapped those two methods:
idea.findOppositeColors( Color.RED )
idea.findColor( Color.RED )
These types of errors are really hard to track down especially when those lines aren't right next to each other. You can completely break your program by just adding a new call in anywhere between those two lines and get wildly different results. Sure when we're dealing with small number of lines it's easy to spot errors. But, in a larger program you can waste days trying to reproduce them even though the data in the program hasn't changed.
And this only looks at single threaded problems. If BadIdea was being used in a multi-threaded situation the errors can get really bizarre. What happens if findColors() and findOppositeColors() is called at the same time? Crash, all your hair falls out, Death, space and time collapse into a singularity and the universe is swallows up? Probably at least two of those. Threads are probably above your head now, but hopefully we can steer you away from doing bad things now so when you do get to threads those bad practices don't cause you real heartache.
Did you notice how careful you had to be when calling the methods? They overwrote each other, they shared memory possibly randomly, you had to remember the details of how it worked on the inside to make it work on the outside, changing the order in which things were called produce very big changes in the next lines down, and it only could only work in a single thread situation. Doing things like this will produce really brittle code that seems to fall apart whenever you touch it. These practices I showed contributed directly to the code being brittle.
While this might look like encapsulation it is the exact opposite because the technical details of how you wrote it have to be known to the caller. The caller has to write their code in a very particular way to make their code work, and they can't do it without knowing about the technical details of your code. This is often called a Leaky Abstraction because the class is suppose to hide the technical details behind an abstraction/interface, but the technical details leak out forcing the caller to change their behavior. Every solution has some degree of leaky-ness, but using any of the above techniques like these guarantees no matter what problem you are trying to solve it will be terribly leaky if you apply them. So let's look at the GoodIdea now.
Let's rewrite using local variables:
public class GoodIdea {
...
public List<Integer> findColor( Color value ) {
List<Integer> results = new ArrayList<Integer>();
for( int i = 0; i < map.length; i++ ) {
if( map[index] == value ) {
results.add( i );
}
}
return results;
}
public List<Integer> findOppositeColors( Color value ) {
List<Integer> results = new ArrayList<Integer>();
for( int i = 0; i < map.length; i++ ) {
if( map[index] != value ) {
results.add( i );
}
}
return results;
}
}
This fixes every problem we discussed above. I know I'm not keeping track of counter or returning it, but if I did I can create a new class and return that instead of List. Sometimes I use the following object to return multiple results quickly:
public class Pair<K,T> {
public K first;
public T second;
public Pair( K first, T second ) {
this.first = first;
this.second = second;
}
}
Long answer, but a very important topic.
Use instance variables when it's a core concept of your class. If you're iterating, recursing or doing some processing, then use local variables.
When you need to use two (or more) variables in the same places, it's time to create a new class with those attributes (and appropriate means to set them). This will make your code cleaner and help you think about problems (each class is a new term in your vocabulary).
One variable may be made a class when it is a core concept. For example real-world identifiers: these could be represented as Strings, but often, if you encapsulate them into their own object they suddenly start "attracting" functionality (validation, association to other objects, etc.)
Also (not entirely related) is object consistency - an object is able to ensure that its state makes sense. Setting one property may alter another. It also makes it far easier to alter your program to be thread-safe later (if required).
Local variables internal to methods are always prefered, since you want to keep each variable's scope as small as possible. But if more than one method needs to access a variable, then it's going to have to be an instance variable.
Local variables are more like intermediate values used to reach a result or compute something on the fly. Instance variables are more like attributes of a class, like your age or name.
The easy way: if the variable must be shared by more than one method, use instance variable, otherwise use local variable.
However, the good practice is to use as more local variables as possible. Why? For your simple project with only one class, there is no difference. For a project that includes a lot of classes, there is big difference. The instance variable indicates the state of your class. The more instance variables in your class, the more states this class can have and then, the more complex this class is, the hard the class is maintained or the more error prone your project might be. So the good practice is to use as more local variable as possible to keep the state of the class as simple as possible.
Short story: if and only if a variable needs to be accessed by more than one method (or outside of the class), create it as an instance variables. If you need it only locally, in a single method, it has to be a local variable.
Instance variables are more costly than local variables.
Keep in mind: instance variables are initialized to default values while local variables are not.
Declare variables to be scoped as narrowly as possible. Declare local variables first. If this isn't sufficient, use instance variables. If this isn't sufficient, use class (static) variables.
I you need to return more than one value return a composite structure, like an array or an object.
Try to think about your problem in terms of objects. Each class represents a different type of object. Instance variables are the pieces of data that a class needs to remember in order to work, either with itself or with other objects. Local variables should just be used intermediate calculations, data that you don't need to save once you leave the method.
Try not to return more than one value from your methods in first place. If you can't, and in some cases you really can't, then I would recommend encapsulating that in a class. Just in last case I would recommend changing another variable inside your class (an instance variable). The problem with the instance variables approach is that it increases side effects - for example, you call method A in your program and it modifies some instance(s) variable(s). Over time, that leads to increased complexity in your code and maintenance becomes harder and harder.
When I have to use instance variables, I try to make then final and initialize then in the class constructors, so side effects are minimized. This programming style (minimizing the state changes in your application) should lead to better code that is easier to maintain.
Generally variables should have minimal scope.
Unfortunately, in order to build classes with minimized variable scope, one often needs to do a lot of method parameter passing.
But if you follow that advice all the time, perfectly minimizing variable scope, you
may end up with a lot of redundancy and method inflexibility with all the required objects passed in and out of methods.
Picture a code base with thousands of methods like this:
private ClassThatHoldsReturnInfo foo(OneReallyBigClassThatHoldsCertainThings big,
AnotherClassThatDoesLittle little) {
LocalClassObjectJustUsedHere here;
...
}
private ClassThatHoldsReturnInfo bar(OneMediumSizedClassThatHoldsCertainThings medium,
AnotherClassThatDoesLittle little) {
...
}
And, on the other hand, imagine a code base with lots of instance variables like this:
private OneReallyBigClassThatHoldsCertainThings big;
private OneMediumSizedClassThatHoldsCertainThings medium;
private AnotherClassThatDoesLittle little;
private ClassThatHoldsReturnInfo ret;
private void foo() {
LocalClassObjectJustUsedHere here;
....
}
private void bar() {
....
}
As code increases, the first way may minimize variable scope best, but can easily lead to a lot of method parameters being passed around. The code will usually be more verbose and this can lead to a complexity as one refactors all these methods.
Using more instance variables can reduce the complexity of lots of method parameters being passed around and can give a flexibility to methods when you are frequently reorganizing methods for clarity. But it creates more object state that you have to maintain. Generally the advice is to do the former and refrain from the latter.
However, very often, and it may depend on the person, one can more easily manage state complexity compared with the thousands of extra object references of the first case. One may notice this when business logic within methods increases and organization needs to change to keep order and clarity.
Not only that. When you reorganize your methods to keep clarity and make lots of method parameter changes in the process, you end up with lots of version control diffs which is not so good for stable production quality code. There is a balance. One way causes one kind of complexity. The other way causes another kind of complexity.
Use the way that works best for you. You will find that balance over time.
I think this young programmer has some insightful first impressions for low maintenance code.
Use instance variables when
If two functions in the class need the same value, then make it an instance variable
or
If the state is not expected to change, make it an instance variable. For example: immutable object, DTO, LinkedList, those with final variables
or
If it is an underlying data on whom actions are performed. For example: final in arr[] in the PriorityQueue.java source code file
or
Even if it is used only once and state is expected to change, make it an instance if it is used only once by a function whose parameter list should be empty. For example: HTTPCookie.java Line: 860 hashcode() function uses 'path variable'.
Similarly, use a local variable when none of these conditions match, specifically if the role of the variable would end after the stack is popped off. For example: Comparator.compare(o1, o2);