Assign final variable in a try block - java

Very short question: Is there a more elegant way to do this:
Object tmp;
try {
tmp = somethingThatCanFail();
} catch (Fail f) {
tmp = null;
}
final Object myObject = tmp;
// now I have a final myObject, which can be used in anonymous classes

You could extract the creation of the value in its own method:
final Object myObject = getObjectOrNull();
public Object getObjectOrNull() {
try{
return somethingThatCanFail();
} catch (Fail f) {
return null;
}
}
It's longer, but depending on your definition of "elegant" it might be more elegant.

Depends what you mean by "this" (and "more elegant")
I'm not sure why you think you need tmp AND myObject, but there's no way to avoid having one of those declarations outside the try block IF you want to access it in the catch block.
What's wrong with
Object myObject = null;
try {
myObject = somethingThatCanFail();
} catch (Fail f) {
// do nothing because we can deal with myObject being null just fine
}

These days I tend to do it like this
final Thingy zeFing; {
Thingy t = null;
try {
t = somethingThatCanFail();
} catch (CurveBall f) {
// log...
}
zeFing = t;
}

Related

How can I solve LI_LAZY_INIT_UPDATE_STATIC?

I'm trying to initialize a MethodHandle for a non-public method in an upstream library.
private static Method OF_METHOD;
static Method ofMethod() {
if (OF_METHOD == null) {
try {
OF_METHOD = RequestObject.class.getDeclaredMethod(
"of", Class.class, String.class, String.class,
Object.class, Object.class);
if (!OF_METHOD.isAccessible()) {
OF_METHOD.setAccessible(true);
}
} catch (final NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
}
return OF_METHOD;
}
private static MethodHandle OF_HANDLE;
static MethodHandle ofHandle() {
if (OF_HANDLE == null) {
try {
OF_HANDLE = MethodHandles.lookup().unreflect(ofMethod());
} catch (final ReflectiveOperationException roe) {
throw new RuntimeException(roe);
}
}
return OF_HANDLE;
}
And my SpotBugs Bug Detecter Report says the ofMethod() has a LI_LAZY_INIT_UPDATE_STATIC problem.
I understand what it's saying. I see those two steps(assigning and setting accessible) are problematic in multi-threaded environment.
How can I solve the problem? Should I apply Double-checked locking?
Or should I put ofMethod() logic into ofHandle()?
I'm answering for my own question.
The idea of holding a lazy object reference is a bad idea.
Even with the Double-checked locking,
private static volatile Method OF_METHOD;
static Method ofMethod() {
Method ofMethod = OF_METHOD;
if (ofMethod == null) {
synchronized (JacksonRequest.class) {
ofMethod = OF_METHOD;
if (ofMethod == null) {
try {
ofMethod = ...;
} catch (final NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
if (!ofMethod.isAccessible()) {
ofMethod.setAccessible(true);
}
OF_METHOD = ofMethod;
}
}
}
return ofMethod;
}
Anyone can change the accessible state.
I ended up with following code which doesn't depend on any external variables.
static Method ofMethod() {
try {
final Method ofMethod = ...;
if (!ofMethod.isAccessible()) {
ofMethod.setAccessible(true);
}
return ofMethod;
} catch (final NoSuchMethodException nsme) {
throw new RuntimeException(nsme);
}
}

Ho to fix error "not able to initialize final variable in static block"?

I have an error where I can "Not able to initialize final variable "classpath" in static block"
class A {
static final String classPath;
static {
try {
classPath = new ClassPathResource("").getFile().toString();
} catch (IOException e) {
e.printStackTrace();
}
}
}
How can I circumvent or untie it? Thx
The problem is that you're not assigning the variable in the exception case.
Assuming that you actually need the value of classPath to be correct, you may as well throw an exception, to indicate that something has gone terminally wrong, and this class cannot be used:
static {
try {
classPath = ...
} catch (IOException e) {
throw new RuntimeException(e);
}
}
(And if you don't actually need it, remove the field!)
Whilst it doesn't make sense to set a default value, it's instructive to look at why you can't set one in the catch block.
try {
classPath = ...
} catch (IOException e) {
classPath = defaultValue; // classPath may already be assigned.
}
This is because of the rules of definite assignment for try statements:
V is definitely unassigned before a catch block iff all of the following are true:
V is definitely unassigned after the try block.
...
So, without looking at the other things which must be true, V is not definitely unassigned after the try block (i.e. if the assignment succeeds), so it is not definitely unassigned in the catch block; as such, you can't assign another value.
Note that if you want to assign a default value, you would need to either assign a local variable first:
String classPath;
try {
classPath = ...
} catch (IOException e) {
classPath = defaultValue;
}
A.classPath = classPath;
Or define a static method:
class A {
static final String classPath = getClassPath();
static String getClassPath() {
try {
return ...;
} catch (IOException e) {
return defaultValue;
}
}
}
(The advantage of the latter being that you can invoke it in unit tests; the disadvantage is that it breaks checking that any static variables used in the method (or other methods called) are actually initialized).
The logic is correct, but you need to initialize the value not matter what. Here, in case of an exception, you don't do it.
You need to manage the case where you have an exception :
Default value ( a valid is better ! )
Of course, you can't just write two statement that would instantiate it, you can use a local variable
String s;
try{
s = new ClassPathResource("").getFile().toString();
} catch (IOException e){
s = MyDefaultValue;
}
classPath = s;
Or let a static method to do it for you (to much logic in a static block is messy)
throw a RuntimeException, this will be valid and will stop the execution. (Thanks #Andy Turner and his example)
class A {
private static final String classPath;
static {
String tempPath=null;
try {
tempPath=new ClassPathResource("").getFile().toString();
} catch (IOException e) {
e.printStackTrace();
}
classPath=tempPath;
}
}
It will work.

Constant variable might already have been assigned

I really need help. Don't know how to fix this. I wanna load some constants from properties file. I think, that Catch with FileNotFoundException can be thrown only on the properties.load(). But IDE says that
VK_MIN_ID = VK_MIN_ID_DEFAULT;
VK_MAX_ID = VK_MAX_ID_DEFAULT;
MAX_USERS_TO_PARSE_AT_ONCE = MAX_USERS_TO_PARSE_AT_ONCE_DEFAULT;
this variables might already have been assigned. Where?
Full code:
private static final int VK_MIN_ID;
private static final int VK_MIN_ID_DEFAULT = 1;
private static final int VK_MAX_ID;
private static final int VK_MAX_ID_DEFAULT = 999_999_999;
private static final int MAX_USERS_TO_PARSE_AT_ONCE;
private static final int MAX_USERS_TO_PARSE_AT_ONCE_DEFAULT = 500;
static {
FileInputStream fileInputStream;
Properties properties = new Properties();
try {
fileInputStream = new FileInputStream("src/main/resources/vk.properties");
properties.load(fileInputStream);
if (properties.containsKey("vk.min.id")) {
VK_MIN_ID = Integer.parseInt(properties.getProperty("vk.min.id"));
} else {
VK_MIN_ID = VK_MIN_ID_DEFAULT;
}
if (properties.containsKey("vk.max.id")) {
VK_MAX_ID = Integer.parseInt(properties.getProperty("vk.max.id"));
} else {
VK_MAX_ID = VK_MAX_ID_DEFAULT;
}
if (properties.containsKey("max.users.to.parse.at.once")) {
MAX_USERS_TO_PARSE_AT_ONCE = Integer.parseInt(properties.getProperty("max.users.to.parse.at.once"));
} else {
MAX_USERS_TO_PARSE_AT_ONCE = MAX_USERS_TO_PARSE_AT_ONCE_DEFAULT;
}
} catch (FileNotFoundException e) {
logger.warn("Файл свойств отсуствует! Устанавливаем настройки по умолчанию...");
VK_MIN_ID = VK_MIN_ID_DEFAULT;
VK_MAX_ID = VK_MAX_ID_DEFAULT;
MAX_USERS_TO_PARSE_AT_ONCE = MAX_USERS_TO_PARSE_AT_ONCE_DEFAULT;
} catch (IOException e) {
logger.error("Ошибка чтения файла свойств! Работа будет прекращена", e);
}
}
Big thanks.
The Java compiler doesn't see what we can see -- that the assignment to MAX_USERS_TO_PARSE_AT_ONCE is at the end of the try block, so that either it gets assigned there or in the catch block. It sees a possibility that it gets assigned in the try block, an exception is thrown, and it gets assigned again in the catch block.
Use a temporary variable to work around this.
int temp;
try {
// other code
if (properties.containsKey("max.users.to.parse.at.once")) {
temp = Integer.parseInt(properties.getProperty("max.users.to.parse.at.once"));
} else {
temp = MAX_USERS_TO_PARSE_AT_ONCE_DEFAULT;
}
} catch (FileNotFoundException e) {
// other code.
temp = MAX_USERS_TO_PARSE_AT_ONCE_DEFAULT;
} catch (IOException e) {
// You'll need to assign something to temp here too.
}
After the last catch block, then assign temp to your final variable, once.
MAX_USERS_TO_PARSE_AT_ONCE = temp;
This way you can keep MAX_USERS_TO_PARSE_AT_ONCE final.
Instead of having a try-catch, and having values set in the catch block, you can replace all that with a simple if-else, and that will work without having to remove the final. Something like this:
File f = new File("src/main/resources/vk.properties");
if (f.exists()) {
// set values here from FileInputStream
// ...
} else {
// set alternate values here if the file is not found.
VK_MIN_ID = VK_MIN_ID_DEFAULT;
VK_MAX_ID = VK_MAX_ID_DEFAULT;
MAX_USERS_TO_PARSE_AT_ONCE = MAX_USERS_TO_PARSE_AT_ONCE_DEFAULT;
}
EDIT
The reason why you can't assign the constants from the catch block, is because, even though you know that the exception will be caught before the values are set for the first time, the compiler doesn't know that for a fact. It has to assume that the exception can be thrown anywhere in the try block. And so, from the perspective of the compiler, it is possible for the final variables to have been set before the exception is caught, so it gives you the error.
The problem is this:
VK_MIN_ID = VK_MIN_ID_DEFAULT;
VK_MAX_ID = VK_MAX_ID_DEFAULT;
MAX_USERS_TO_PARSE_AT_ONCE = MAX_USERS_TO_PARSE_AT_ONCE_DEFAULT;
VK_MIN_ID, VK_MAX_ID and MAX_USERS_TO_PARSE_AT_ONCE are declared as final, this means that once assigned you can't change their value. Remove final keyword.

Variable has not accessed from inner class

I have inner class which run code in UI thread and I need to pass variable to run() method.
I try to pass final res without array but I've got an error that it's necessary to use array res[0].
In this case I need to initialize res[] because it throws NullPointerException.
Are there any other way to pass variable into inner class?
private String sendRequest(String url, String... data) {
final Connection.Response[] res = {};
...
try {
final Connection connection = Jsoup.connect(url)
.method(Connection.Method.POST)
.cookies(cookies)
.timeout(30000)
.ignoreContentType(true);
if (data != null) {
connection.data(data);
}
((Activity) con).runOnUiThread(new Runnable() {
#Override
public void run() {
try {
res[0] = connection.execute();
} catch (IOException e) {
e.printStackTrace();
}
}
});
result = Jsoup.parse(res[0].parse().outerHtml(), "UTF-8").text();
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
res[] is defined as an empty array (no locations), then you try to assign to something to location 0 in res[], there is no location 0 because it needs to be given a size...
final Connection.Response[] res = new Connection.Response[requiredArraySize];
In your case, requiredArraySize is probably 1.

modify method argument or return in java

I have seen this method in android source code.
protected LocaleConfiguration doInBackground(Void... unused) {
LocaleConfiguration localeConfiguration = new LocaleConfiguration();
readConfiguration(Launcher.this, localeConfiguration);
return localeConfiguration;
}
private static void readConfiguration(Context context, LocaleConfiguration configuration) {
DataInputStream in = null;
try {
in = new DataInputStream(context.openFileInput(PREFERENCES));
configuration.locale = in.readUTF();
configuration.mcc = in.readInt();
configuration.mnc = in.readInt();
} catch (FileNotFoundException e) {
// Ignore
} catch (IOException e) {
// Ignore
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// Ignore
}
}
}
}
why not something like this
private static LocaleConfiguration readConfiguration(Context context) {
LocaleConfiguration configuration = new LocaleConfiguration();
DataInputStream in = null;
try {
in = new DataInputStream(context.openFileInput(PREFERENCES));
configuration.locale = in.readUTF();
configuration.mcc = in.readInt();
configuration.mnc = in.readInt();
} catch (FileNotFoundException e) {
// Ignore
} catch (IOException e) {
// Ignore
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
// Ignore
}
}
}
return configuration;
}
what is the advantage of modifying argument instead of returning new value
The advantage of modifying an argument instead of returning a new instance is that you put control of instantiation in the hands of the calling code - i.e. you allow it to re-use an existing instance.
The 'modifying argument' approach allows you to initialise an object using several such methods. e.g.
LocaleConfiguration localeConfiguration = new LocaleConfiguration();
readConfiguration(Launcher.this, localeConfiguration);
readSomeOtherConfiguration(Launcher.this, localeConfiguration);
return localeConfiguration;
Arguably you could do the same thing by returning the same instance as was passed in as a parameter, but I personally think that's asking for trouble.
Another possible reason might be if the cost of instantiation was high, you might want to recycle an old object. This doesn't appear to be the case with the code you present though, and it's an optimisation so only think about doing this if absolutely necessary!
Personally I would tend to take the 'return a new instance' approach unless there's a specific reason not to. I think it's simpler and decreases the likelihood of subtle errors in the calling code.

Categories

Resources