I've written seven test cases for understanding the behavior of the finally block. What is the logic behind how finally works?
package core;
public class Test {
public static void main(String[] args) {
new Test().testFinally();
}
public void testFinally() {
System.out.println("One = " + tryOne());
System.out.println("Two = " + tryTwo());
System.out.println("Three = " + tryThree());
System.out.println("Four = " + tryFour());
System.out.println("Five = " + tryFive());
System.out.println("Six = " + trySix());
System.out.println("Seven = " + trySeven());
}
protected StringBuilder tryOne() {
StringBuilder builder = new StringBuilder();
try {
builder.append("Cool");
return builder.append("Return");
}
finally {
builder = null;
}
}
protected String tryTwo() {
String builder = "Cool";
try {
return builder += "Return";
}
finally {
builder = null;
}
}
protected int tryThree() {
int builder = 99;
try {
return builder += 1;
}
finally {
builder = 0;
}
}
protected StringBuilder tryFour() {
StringBuilder builder = new StringBuilder();
try {
builder.append("Cool");
return builder.append("Return");
}
finally {
builder.append("+1");
}
}
protected int tryFive() {
int count = 0;
try {
count = 99;
}
finally {
count++;
}
return count;
}
protected int trySix() {
int count = 0;
try {
count = 99;
}
finally {
count = 1;
}
return count;
}
protected int trySeven() {
int count = 0;
try {
count = 99;
return count;
}
finally {
count++;
}
}
}
Why builder = null is not working?
Why does builder.append("+1") work whereas count++( in trySeven()) does not work?
Once you do the return, the only way to override that is to do another return (as discussed at Returning from a finally block in Java, this is almost always a bad idea), or otherwise complete abruptly. Your tests don't ever return from a finally.
JLS §14.1 defines abrupt completion. One of the abrupt completion types is a return. The try blocks in 1,2,3,4, and 7 abruptly complete due to returns. As explained by §14.20.2, if the try block completes abruptly for a reason R besides a throw, the finally block is immediately executed.
If the finally block completes normally (which implies no return, among other things), "the try statement completes abruptly for reason R.". In other words, the return initiated by the try is left intact; this applies to all your tests. If you return from the finally, "the try statement completes abruptly for reason S (and reason R is discarded)." (S here being the new overriding return).
So in tryOne, if you did:
finally {
builder = null;
return builder;
}
this new return S would override the original return R.
For builder.append("+1") in tryFour, keep in mind StringBuilder is mutable, so you're still returning a reference to the same object specified in the try. You're just doing a last minute mutation.
tryFive and trySix are straight-forward. Since there is no return in the try, the try and finally both complete normally, and it executes the same as if there was no try-finally.
Let's start with use case you'll see more often - you have a resource that you must close to avoid a leak.
public void deleteRows(Connection conn) throws SQLException {
Statement statement = conn.createStatement();
try {
statement.execute("DELETE * FROM foo");
} finally {
statement.close();
}
}
In this case, we have to close the statement when we're done, so we don't leak database resources. This will ensure that in the case of an Exception being thrown, we will always close our Statement before the function exits.
try { ... } finally { ... } blocks are meant for ensuring that something will always execute when the method terminates. It's most useful for Exception cases. If you find yourself doing something like this:
public String thisShouldBeRefactored(List<String> foo) {
try {
if(foo == null) {
return null;
} else if(foo.length == 1) {
return foo.get(0);
} else {
return foo.get(1);
}
} finally {
System.out.println("Exiting function!");
}
}
You're not really using finally properly. There is a performance penalty to this. Stick to using it when you have Exception cases that you must clean up from. Try refactoring the above to this:
public String thisShouldBeRefactored(List<String> foo) {
final String result;
if(foo == null) {
result = null;
} else if(foo.length == 1) {
result = foo.get(0);
} else {
result = foo.get(1);
}
System.out.println("Exiting function!");
return result;
}
The finally block is executed when you leave the try block. The "return" statement does two things, one it sets the return value of the function and two it exits the function. Normally this would look like an atomic operation but within a try block it will cause the finally block to execute after the return value was set and before the function exits.
Return execution:
Assign return value
run finally blocks
exit function
Example one (primitive):
int count = 1;//Assign local primitive count to 1
try{
return count; //Assign primitive return value to count (1)
}finally{
count++ //Updates count but not return value
}
Example two(reference):
StringBuilder sb = new StringBuilder();//Assign sb a new StringBuilder
try{
return sb;//return a reference to StringBuilder
}finally{
sb.append("hello");//modifies the returned StringBuilder
}
Example three (reference):
StringBuilder sb = new StringBuilder();//Assign sb a new StringBuilder
try{
return sb;//return a reference to StringBuilder
}finally{
sb = null;//Update local reference sb not return value
}
Example four (return):
int count = 1; //assign count
try{
return count; //return current value of count (1)
}finally{
count++; //update count to two but not return value
return count; //return current value of count (2)
//replaces old return value and exits the finally block
}
builder = null and builder.append("+1") are working. It's just that they're not affecting what you're returning. The function returns what the return statement has, regardless of what happens afterward.
The reason there is a difference is because builder is passed by reference. builder=null changes the local copy of builder. builder.append("+1") affects the copy held by the parent.
Why builder = null is not working?Because you are setting the local reference to null which will not change the content of the memory. So it is working, if you try to access the builder after finally block then you'll get null.Why builder.append("+1") work? Because you are modifying the content of the memory using the reference,that's why it should work.Why count++ does not work in testFive()? It is working fine with me. It outputs 100 as expected.
Consider what the compiler is actually doing for the return statement, for instance in tryOne(): it copies a reference to builder back to the calling function's environment. After it's done this, but before control goes back to the calling function, the finally block executes. So you have something more like this, in practice:
protected StringBuilder tryOne() {
StringBuilder builder = new StringBuilder();
try {
builder.append("Cool");
builder.append("Return");
StringBuilder temp = builder;
return temp;
} finally {
builder = null;
}
}
Or, in terms of the order that statements actually get executed (ignoring possible exceptions, of course), it looks more like this:
protected StringBuilder tryOne() {
StringBuilder builder = new StringBuilder();
builder.append("Cool");
builder.append("Return");
StringBuilder temp = builder;
builder = null;
return temp;
}
So setting builder = null does run, it just doesn't do anything useful. However, running builder.append("something") will have a visible effect, since both temp and builder refer to the same (mutable) object.
Likewise, what's really happening in trySeven() is something more like this:
protected int trySeven() {
int count = 0;
count = 99;
int temp = count;
count++;
return temp;
}
In this case, since we're dealing with an int, the copies are independent, so incrementing one doesn't affect the other.
All that said, the fact remains that putting return statements in a try-finally block is quite clearly confusing, so if you've got any kind of choice in the matter, you'd be better off rewriting things so that all your return statements are outside any try-finally blocks.
Related
I'm returning a value from an array, but I want to set the value to null afterwards . The problem is I keep getting an error. Why is this?
public Book retrieveBookFromBookshelf (String title)
{
for (int i = 0; i < this.books.length; i++) {
if (this.books[i].getTitle().equals(title)) {
return this.books[i];
this.books[i] = null;
}
}
return null;
}
Because before you set the value to null, you return from the function. Once return is executed, nothing else is done in the current function and control is given back to the caller function.
What you're attempting to do is not possible. instead, cache the reference to this.books[i].
if (this.books[i].getTitle().equals(title)) {
Book book = this.books[i]; // cache the reference
this.books[i] = null;
return book;
}
You can't normally run statements after returning. You need to store the value in a temporary variable:
Book result = this.books[i];
this.books[i] = null;
return result;
Alternatively, you can return in a try block and set it to null inside finally:
try {
return this.books[i];
} finally {
this.books[i] = null;
}
But I think that's a little overkill for your use case.
The following code comes from this answer
try {
// get all the interfaces
List<NetworkInterface> all = Collections.list(NetworkInterface.getNetworkInterfaces());
//find network interface wlan0
for (NetworkInterface networkInterface : all) {
if (!networkInterface.getName().equalsIgnoreCase("wlan0")) continue;
//get the hardware address (MAC) of the interface
byte[] macBytes = networkInterface.getHardwareAddress();
if (macBytes == null) {
return "";
}
StringBuilder res1 = new StringBuilder();
for (byte b : macBytes) {
//gets the last byte of b
res1.append(Integer.toHexString(b & 0xFF) + ":");
}
if (res1.length() > 0) {
res1.deleteCharAt(res1.length() - 1);
}
return res1.toString();
}
} catch (Exception ex) {
ex.printStackTrace();
}
I get error Cannot return a value with void result type on those 2 lines: return ""; and return res1.toString(); I put the code inside public void onStart() How do I fix this, and can you tell me the cause of this problem?
Instead of returning an empty String, just return;
Methods that are void do not return anything but you can use return statement to terminate an operation if some condition is not met!
I hope this helps!
You need to change the line
public void onStart()
To
public String onStart()
This is because you are returning a String, whereas a void function does not return any data.
If the method cannot be changed to a String return type then you could just put the string into a variable that you declare earlier in the program and then use
return;
To exit the method.
Issue is clear, you are returning a value for a function public void onStart(). You are declaring the return type as void and yet you have return statements.
Try different ways to return the value, like put it in request/session or static variable(not recommanded) etc
My problem is, that I want to return an Object from the ArrayList "blocks".
My code doesn't work - error says This method must return a result of type block
public block getBlockUnderneath (int x, int y){
for(int i = 0; i<blocks.size(); i++){
if (blocks.get(i).x == x) {
return blocks.get(i);
}
}
}
You have two issues:
If blocks.size()==0 your method returns nothing
If none of the blocks in blocks have block.x==x your method returns nothing.
In Java a method must return a value of it is declared to do so.
The easiest solution to your issue is to return null at the end of the method:
public block getBlockUnderneath (int x, int y){
for(final block b : blocks){
if (b.x == x) {
return b;
}
}
return null;
}
Notice this uses an enhanced-for-loop, this is the recommended way to loop over Collections (or anything that implements Iterable<T>) in Java.
A better approach might be to throw an exception if no item is found:
public block getBlockUnderneath (int x, int y){
for(final block b : blocks){
if (b.x == x) {
return b;
}
}
throw new NoSuchElementException();
}
In either case you would need to handle the corner case in code that calls this method.
P.S. please stick to Java naming conventions. Classes should be in PascalCase - so you block class should be called Block.
Just for fun, in Java 8:
public block getBlockUnderneath(int x, int y) {
return blocks.stream().filter((b) -> b.x == x).findFirst().get();
}
The problem with your method is that there exists a scenario in which the return block is not executed. In that case, when a method is not declared to be void, you must declare the exit point for it.
You can exit using return or throw an exception. The choice depends on what your program should do if the requested value could not be found.
public block getBlockUnderneath (int x, int y){
for(int i = 0; i<blocks.size(); i++){
if (blocks.get(i).x == x) {
return blocks.get(i);
}
}
return null; //or throw NoSuchElementException or IllegalStateException
}
What's more you can improve you code by using a for-each loop. This solution may give you better performance and also code security as it uses an iterator rather than accessing item by index which is not necessarily efficient.
In this case you access the same item twice.
if (blocks.get(i).x == x) {
return blocks.get(i);
}
Full example
public Block findBlock(int x} { //The class name is Block
for(Block block : blocks) {
if(block.x == x {
return block;
}
}
return null;
}
Be also aware of that returning null may cause problems and thus is considered bad practice. You can avoid null, thanks to checked exceptions, default values or using Null object
There is a native implementation of this common coding pattern in Java 8. Using the Optional<T> class from the Guava library can solve this problem for versions of Java < 8.
public Optional<Block> findBlock(int x} { //The class name is Block
for(Block block : blocks) {
if(block.x == x {
return Optional.of(block);
}
}
return Optional.empty();
}
Usage
public void someActionWithBlocK() {
Optional<Block> block = findBlock(5);
if(block.isPresent()) {
//some action with block
}
}
You could never loop.
If you have a return statement inside of a loop, then the compiler doesn't take the bonafide guarantee that the loop will execute and that you will return. To get around that, you must also return after your loop.
Or, better yet, have one variable to return, like such:
block ret = null;
for(block b : blocks) {
if(b.x == x) { // I'm going to go over this in a mo
ret = b;
break;
}
}
return ret;
What does the return inside the if statements do in the following code?
public void startElement(String namespaceURI, String localName,String qName,
Attributes atts) throws SAXException
{
depth++;
if (localName.equals("channel"))
{
currentstate = 0;
return;
}
if (localName.equals("image"))
{
// record our feed data - you temporarily stored it in the item :)
_feed.setTitle(_item.getTitle());
_feed.setPubDate(_item.getPubDate());
}
if (localName.equals("item"))
{
// create a new item
_item = new RSSItem();
return;
}
if (localName.equals("title"))
{
currentstate = RSS_TITLE;
return;
}
if (localName.equals("description"))
{
currentstate = RSS_DESCRIPTION;
return;
}
if (localName.equals("link"))
{
currentstate = RSS_LINK;
return;
}
if (localName.equals("category"))
{
currentstate = RSS_CATEGORY;
return;
}
if (localName.equals("pubDate"))
{
currentstate = RSS_PUBDATE;
return;
}
// if you don't explicitly handle the element, make sure you don't wind
// up erroneously storing a newline or other bogus data into one of our
// existing elements
currentstate = 0;
}
Does it takes us out of the if statement and proceeds to next statement or it takes us out of the method startElement?
The returns in the above code will take you out of the method.
It finishes the method so the code below it, is not executed.
Does it takes us out of the if statement and proceeds to next
statement or it takes us out of the method startElement?
It takes you out of the method..
The return statement terminates the execution of a function
return always takes control out of calling method.
Yes. The return here will take the control out of method.
it will return what you declared in the method head (here void = nothing = it will just end the method)
The return here is probably used in order to "improve" the performance of the method, so that other comparisons are not executed, once the needed scenario is performed.
However, it's not good practice to have multiple return points in a method.
As stated in my comments I'd try a different approach to achieve the flow of the code in question.
The return will end the flow of the method, and is functionally identical to using a shorter else if chain like
/* if (localName.equals("channel")) {
currentstate = 0; // This can be removed because it's the default below.
} else */ if (localName.equals("image")) {
// record our feed data - you temporarily stored it in the item :)
_feed.setTitle(_item.getTitle());
_feed.setPubDate(_item.getPubDate());
} else if (localName.equals("item")) {
// create a new item
_item = new RSSItem();
} else if (localName.equals("title")) {
currentstate = RSS_TITLE;
} else if (localName.equals("description")) {
currentstate = RSS_DESCRIPTION;
} else if (localName.equals("link")) {
currentstate = RSS_LINK;
} else if (localName.equals("category")) {
currentstate = RSS_CATEGORY;
} else if (localName.equals("pubDate")) {
currentstate = RSS_PUBDATE;
} else {
currentstate = 0;
}
All,
I have implemented a code that generates 2 random prime numbers and the multiplication of these 2 numbers should pass the Miller Rabin primality test. However, my code keeps looping all the time trying to find a number that passes Miller rabin test and ends up with a Stackoverflow exception. Here is the code:
private void populateRandomPrimes()
{
onePrimeValue = RandomPrime.getValue();
do
{
secondPrimeValue= RandomPrime.getValue();
}while(onePrimeValue == secondPrimeValue);
BigInteger calcNum = new BigInteger(Integer.toString(onePrimeValue*secondPrimeValue));
try
{
**if (calcNum.isProbablePrime(20))**
populateMultiplicativeForPlayer();
else
populateRandomPrimes();
}
catch (Exception io)
{
io.printStackTrace();
}
}
In the code above:
1 > RandomPrime class returns a random prime number
2 > Both onePrimeValue and secondPrimeValue should be different
3 > Since the code line : if (calcNum.isProbablePrime(20)) never returns a true, I end up calling the same till I get Stackoverflow exception
Can anyone suggest me how to get around with this issue?
Please see my comment below your question...
private void populateRandomPrimes()
{
while (true){
onePrimeValue = RandomPrime.getValue();
do
{
secondPrimeValue= RandomPrime.getValue();
}while(onePrimeValue == secondPrimeValue);
BigInteger calcNum = new BigInteger(Integer.toString(onePrimeValue*secondPrimeValue));
try
{
if (calcNum.isProbablePrime(20)){
populateMultiplicativeForPlayer();
return;
}
}
catch (Exception io)
{
io.printStackTrace();
}
}
}
Move the calcNum computation inside of the do-while loop and add an extra condition:
private void populateRandomPrimes()
{
onePrimeValue = RandomPrime.getValue();
BigInteger calcNum = null;
do {
secondPrimeValue= RandomPrime.getValue();
calcNum = new BigInteger(Integer.toString(onePrimeValue*secondPrimeValue));
} while(onePrimeValue.equals(secondPrimeValue) && !(calcNum.isProbablePrime(20));
//if you get here, calcNum isProbPrime, so no need to check again
try {
populateMultiplicativeForPlayer();
} catch (Exception ex) {
ex.printStackTrace();
}
}
You are running into this problem because you don't have a definitive base case for your recursion. Also, don't use == on objects unless you know what you're doing. Your do-while condition should have used .equals() to compare onePrimeValue and secondPimeValue
The worst case scenario with this approach is that your program will get stuck in an infinite loop because the exit condition of the do-while loop is never satisfied. To remedy this, you can add a third condition to the loop to ensure that it terminates after a fixed number of iterations.
private BigInteger generateAppropriateNumber() {
BigInteger result;
boolean isOk = false;
while (isOk == false) {
onePrimeValue = RandomPrime.getValue();
do {
secondPrimeValue= RandomPrime.getValue();
} while(onePrimeValue.equals(secondPrimeValue));
BigInteger result =
new BigInteger(Integer.toString(onePrimeValue * secondPrimeValue));
if (result.isProbablePrime(20)) {
isOk = true;
}
}
return result;
}
private void populateRandomPrimes() {
populateMultiplicativeForPlayer(generateAppropriateNumber());
}
I.e.
Use loop instead of recursion to avoid stack increase.
Don't use global variables. It is really bad style.