recursion in java without local variables - java

I was currently reviewing some code from a colleague and found a use of recursion I have never seen before.
I reduced it, so what he basically does is:
public class Main {
static class Test {
private static final int MAX = 10;
private int mValue = 0;
void test() {
System.out.println( mValue );
mValue++;
if (mValue < MAX) {
test();
}
}
}
public static void main( final String[] args ) {
final Test test = new Test();
test.test();
}
}
When I would use recursion, I would give all needed variables as parameters and then have a return value giving something back, or using one of the parameters to provide a container object where a result could be written in. So that every recursion has it's own scope of data to work on.
I looked around but also every example on the net does it the way I would do it.
So could this lead to any errors? Some inconsistencies in the object? Is there a difference between this and calling different private methods manipulating the member variable?
brgds,
J

This will work as expected, as long as a single instance of the Test class never runs .test() concurrently.
Edit
#Itay made me realize, that the word "concurrently" is wrong here - it should be "competingly" in the sense of "not cooperating with or not knowing of each other". I am sorry, the german (my native language) word "Konkurrenz" and the english word "Concurrency" have a slightly different meaning.
/Edit
If this is guaranteed, putting mValue in the class scope, not local scope, can improve locality and cache hit rate, also removes some parameter passing - alltogether it trades some performance gain against encapsulation.

As long as your logic is thread-safe (immutable object or containing relevant locking), there's no reason to strictly prefer parameter over global class' field even when recursion occurs.
Of course, as long as, the concerned field represents object's state and not a simple ugly helper variable...

It could be a problem if the recursive method will be called twice on the same instance?

So could this lead to any errors? Some inconsistencies in the object?
In practice, it is not preferred way to implement recursion.
The condition (mValue < MAX) should only be affected within from recursive method. if not, your recursion may end un-expectedly.

Related

What is the difference between the return function and storing a variable?

I'm learning how to code in Java. I'm a little confused by "return;" and what it does and when we use it. Please see the following example of code:
public int something() {
return 1;
}
public static void main() {
int returnValue = something();
System.out.println(returnValue);
//Prints 1
}
Why wouldn't we just store 1 into a int variable called something then use System.out.print(something);
When would we use the return method instead of simply storing into a variable?
Thank you
Sure you could store into a variable but then you would lose one of the very important features, namely the ability to call the method inside itself.
This is relevant for algorithms that divide the work into smaller chunks and invoke themselves on the smaller chunks (and then combine the individual result to a big result). This is very common in sorting algorithms. The technical term is recursion.
Usually the compiler actually does exactly this; creates a variable for storing the value from where the calling code can pick it up. This variable is typically put in the same location - the stack - as the parameters passed in to the called method, and is invisible to your code.
(Also it is needed to make it threadsafe which is essential to utilizing more than one core on a modern cpu).

Why is it a bad idea to use the same field for loop control variables in different methods of a class?

Question from a practice final for Intro to Java Class:
Why is it a bad idea to use the same field for loop control variables in different methods of a class?
A. Because if a method is called from the body of the loop, it may reset the value of the variable used to control that loop
B. Because it’s not a good style to use the same names for variables in different methods
C. Because it wastes memory
D. Because local variables can be kept in CPU registers, and the loop will run faster
The answer is A, but I do not really understand the question in the first place. How does the the variable in the loop affect the second method? And then wouldn't this hold for outside of a loop in a normal method call? If someone could provide an example in code that would be great.
By "same field", I presume the question means the exact same variable, not two different variables with the same name. Like so:
class X {
int i;
void foo() {
for (i = 0; i < 10; ++i) {
bar();
}
}
void bar() {
for (i = 0; i < 20; ++i) {
System.out.println("foobar");
}
}
}
Code like this intends to loop 200 times total, but reusing i in both methods causes one loop to interfere with the other.
To be fair, the question is not worded very well. Code is much clearer than prose.
For example. If you call countToTen and someone reset the counter you will have problems
public class FooExample{
private int countedNumbers = 0;
public void countToTen(){
resetCounter();
while(countedNumbers <= 10){
System.out.println("Record number " + countedNumbers);
countedNumbers++;
}
}
public void resetCounter(){
countedNumbers = 0
}
}
The question doesn't say "the same variable", it says "the same field". "Field" is another name for an instance variable. (a.k.a., "member variable").
IMO, the answer is E: Good programming style/practice is all about making it easier for other programmers to read your code, and the best way to do that is, don't surprise them. In this case, don't surprise them by using a field to store information that is not part of the state of the object. (Loop control variables are virtually never part of an object's state)
But A is a good answer too. (Don't surprise yourself by making the mistake that it describes.) And also, C might be a good answer in some performance-critical application, and answer D might be valid for some JVM implementation.
The only answer that's unconditionally wrong is B because there's nothing inherently wrong with giving different variables the same name if the name succinctly describes what their values mean.

Do You Cache Properties in Local Variables?

Consider the class Foo.
public class Foo {
private double size;
public double getSize() {
return this.size; // Always O(1)
}
}
Foo has a property called size, which is frequently accessed, but never modified, by a given method. I've always cached a property in a variable whenever it is accessed more than once in any method, because "someone told me so" without giving it much thought. i.e.
public void test(Foo foo) {
double size = foo.getSize(); // Cache it or not?
// size will be referenced in several places later on.
}
Is this worth it, or an overkill?
If I don't cache it, are modern compilers smart enough to cache it themselves?
A couple of factors (in no particular order) that I consider when deciding whether or not to store the value returned by a call to a "get() method":
Performance of the get() method - Unless the API specifies, or unless the calling code is tightly coupled with the called method, there are no guarantees of the performance of the get() method. The code may be fine in testing now, but may get worse if the get() methods performace changes in the future or if testing does not reflect real-world conditions. (e.g. testing with only a thousand objects in a container when a real-world container might have ten million) Used in a for-loop, the get() method will be called before every iteration
Readability - A variable can be given a specific and descriptive name, providing clarification of its use and/or meaning in a way that may not be clear from inline calls to the get() method. Don't underestimate the value of this to those reviewing and maintaining the code.
Thread safety - Can the value returned by the get() method potentially change if another thread modifies the object while the calling method is doing its thing? Should such a change be reflected in the calling method's behavior?
Regarding the question of whether or not compilers will cache it themselves, I'm going to speculate and say that in most cases the answer has to be 'no'. The only way the compiler could safely do so would be if it could determine that the get() method would return the same value at every invocation. And this could only be guaranteed if the get() method itself was marked final and all it did was return a constant (i.e an object or primitive also marked 'final'). I'm not sure but I think this is probably not a scenario the compiler bothers with. The JIT compiler has more information and thus could have more flexibility but you have no guarantees that some method will get JIT'ed.
In conclusion, don't worry about what the compiler might do. Caching the return value of a get() method is probably the right thing to do most of the time, and will rarely (i.e almost never) be the wrong thing to do. Favor writing code that is readable and correct over code that is fast(est) and flashy.
I don't know whether there is a "right" answer, but I would keep a local copy.
In your example, I can see that getSize() is trivial, but in real code, I don't always know whether it is trivial or not; and even if it is trivial today, I don't know that somebody won't come along and change the getSize() method to make it non-trivial sometime in the future.
The biggest factor would be performance. If it's a simple operation that doesn't require a whole lot of CPU cycles, I'd say don't cache it. But if you constantly need to execute an expensive operation on data that doesn't change, then definitely cache it. For example, in my app the currently logged in user is serialized on every page in JSON format, the serialization operation is pretty expensive, so in order to improve performance I now serialize the user once when he signs in and then use the serialized version for putting JSON on the page. Here is before and after, made a noticeable improvement in performance:
//Before
public User(Principal principal) {
super(principal.getUsername(), principal.getPassword(), principal.getAuthorities());
uuid = principal.getUuid();
id = principal.getId();
name = principal.getName();
isGymAdmin = hasAnyRole(Role.ROLE_ADMIN);
isCustomBranding= hasAnyRole(Role.ROLE_CUSTOM_BRANDING);
locations.addAll(principal.getLocations());
}
public String toJson() {
**return JSONAdapter.getGenericSerializer().serialize(this);**
}
// After
public User(Principal principal) {
super(principal.getUsername(), principal.getPassword(), principal.getAuthorities());
uuid = principal.getUuid();
id = principal.getId();
name = principal.getName();
isGymAdmin = hasAnyRole(Role.ROLE_ADMIN);
isCustomBranding= hasAnyRole(Role.ROLE_CUSTOM_BRANDING);
locations.addAll(principal.getLocations());
**json = JSONAdapter.getGenericSerializer().serialize(this);**
}
public String toJson() {
return json;
}
The User object has no setter methods, there is no way the data would ever change unless the user signs out and then back in, so in this case I'd say it is safe to cache the value.
If the value of size was calculated each time say by looping through an array and thus not O(1), caching the value would have obvious benefits performance-wise. However since size of Foo is not expected to change at any point and it is O(1), caching the value mainly aids in readability. I recommend continuing to cache the value simply because readability is often times more of a concern than performance in modern computing systems.
IMO, if you are really worried about performance this is a bit overkill or extensive but there is a couple of ways to ensure that the variable is "cached" by your VM,
First, you can create final static variables of the results (as per your example 1 or 0), hence only one copy is stored for the whole class, then your local variable is only a boolean (using only 1 bit), but still maintaining the result value of double (also, maybe you can use int, if it is only 0 or 1)
private static final double D_ZERO = 0.0;
private static final double D_ONE = 1.0;
private boolean ZERO = false;
public double getSize(){
return (ZERO ? D_ZERO : D_ONE);
}
Or if you are able to set the size on initialization of the class you can go with this, you can set the final variable through constructor, and static, but since this is a local variable you can go with the constructor:
private final int SIZE;
public foo(){
SIZE = 0;
}
public double getSize(){
return this.SIZE;
}
this can be accessed via foo.getSize()
In my code, i would cache it if either the getSize() method is time consuming or - and that is more often - the result is used in more or less complex expressions.
For example if calculating an offset from the size
int offset = fooSize * count1 + fooSize * count2;
is easier to read (for me) than
int offset = foo.getSize() * count1 + foo.getSize() * count2;

Passing a parameter versus returning it from function

As it might be clear from the title which approach should we prefer?
Intention is to pass a few method parameters and get something as output. We can pass another parameter and method will update it and method need not to return anything now, method will just update output variable and it will be reflected to the caller.
I am just trying to frame the question through this example.
List<String> result = new ArrayList<String>();
for (int i = 0; i < SOME_NUMBER_N; i++) {
fun(SOME_COLLECTION.get(i), result);
}
// in some other class
public void fun(String s, List<String> result) {
// populates result
}
versus
List<String> result = new ArrayList<String>();
for (int i = 0; i < SOME_NUMBER_N; i++) {
List<String> subResult = fun(SOME_COLLECTION.get(i));
// merges subResult into result
mergeLists(result, subResult);
}
// in some other class
public List<String> fun(String s) {
List<String> res = new ArrayList<String>();
// some processing to populate res
return res;
}
I understand that one passes the reference and another doesn't.
Which one should we prefer (in different situations) and why?
Update: Consider it only for mutable objects.
Returning a value from the function is generally a cleaner way of writing code. Passing a value and modifying it is more C/C++ style due to the nature of creating and destroying pointers.
Developers generally don't expect that their values will be modified by passing it through a function, unless the function explicitly states it modifies the value (and we often skim documentation anyway).
There are exceptions though.
Consider the example of Collections.sort, which does actually do an in place sort of a list. Imagine a list of 1 million items and you are sorting that. Maybe you don't want to create a second list that has another 1 million entries (even though these entries are pointing back to the original).
It is also good practice to favor having immutable objects. Immutable objects cause far fewer problems in most aspects of development (such as threading). So by returning a new object, you are not forcing the parameter to be mutable.
The important part is to be clear about your intentions in the methods. My recommendation is to avoid modifying the parameter when possible since it not the most typical behavior in Java.
You should return it. The second example you provided is the way to go.
First of all, its more clear. When other people read your code, there's no gotcha that they might not notice that the parameter is being modified as output. You can try to name the variables, but when it comes to code readability, its preferable.
The BIG reason why you should return it rather than pass it, is with immutable objects.
Your example, the List, is mutable, so it works okay.
But if you were to try to use a String that way, it would not work.
As strings are immutable, if you pass a string in as a parameter, and then the function were to say:
public void fun(String result){
result = "new string";
}
The value of result that you passed in would not be altered. Instead, the local scope variable 'result' now points to a new string inside of fun, but the result in your calling method still points to the original string.
If you called:
String test = "test";
fun(test);
System.out.println(test);
It will print: "test", not "new string"!
So definitely, it is superior to return. :)
This is more about best practices and your own method to program. I would say if you know this is going to be a one value return type function like:
function IsThisNumberAPrimeNumber{ }
Then you know that this is only going to ever return a boolean. I usually use functions as helper programs and not as large sub procedures. I also apply naming conventions that help dictate what I expect the sub\function will return.
Examples:
GetUserDetailsRecords
GetUsersEmailAddress
IsEmailRegistered
If you look at those 3 names, you can tell the first is going to give you some list or class of multiple user detail records, the second will give you a string value of a email and the third will likely give you a boolean value. If you change the name, you change the meaning, so I would say consider this in addition.
The reason I don't think we understand is that those are two totally different types of actions. Passing a variable to a function is a means of giving a function data. Returning it from the function is a way of passing data out of a function.
If you mean the difference between these two actions:
public void doStuff(int change) {
change = change * 2;
}
and
public void doStuff() {
int change = changeStorage.acquireChange();
change = change * 2;
}
Then the second is generally cleaner, however there are several reasons (security, function visibilty, etc) that can prevent you from passing data this way.
It's also preferable because it makes reusing code easier, as well as making it more modular.
according to guys recommendation and java code convention and also syntax limitation this is a bad idea and makes code harder to understand
BUT you can do it by implementing a reference holder class
public class ReferenceHolder<T>{
public T value;
}
and pass an object of ReferenceHolder into method parameter to be filled or modified by method.
on the other side that method must assign its return into Reference value instead of returning it.
here is the code for getting result of an average method by a ReferenceHolder instead of function return.
public class ReferenceHolderTest {
public static void main(String[] args) {
ReferenceHolder<Double> out = new ReferenceHolder<>();
average(new int[]{1,2,3,4,5,6,7,8},out);
System.out.println(out.value);
}
public static void average(int[] x, ReferenceHolder<Double> out ) {
int sum=0;
for (int a : x) {
sum+=a;
}
out.value=sum/(double)x.length;
}
}
Returning it will keep your code cleaner and cause less coupling between methods/classes.
It is generally preferable to return it.
Specially from a unit testing standpoint. If you are unit testing it
is easier to assert a returned value from a method than verifying if
your object was modified or interacted correctly. (Using
ArgumentCaptor or ArgumentMatcher to assert interactions isn't as
straight forward as a simple return assertion).
Increased code readability. If I see a method that takes 5 object parameters I
might have no immediate way of knowing you plan on modifying one of
those references for future use downstream. Instead if you are returning an
object, I can easily see you ultimately care about the result of that
method's computation.

Java instance variables vs. local variables

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);

Categories