5

I used to declare a final String inside a constructor. Now I want to insert an if-statement in order to declare it differently if needed.

I used to do:

public Config(){
    final String path = "<path>";
    doSomething(path);
}

Now I'm trying

public Config(String mode){
    if (mode = "1") {
        final String path = "<path1>";
    } else {
        final String path = "<path2>";
    }
    doSomething(path);
}

Unfortunately path cannot be found now (cannot find symbol error) and I'm really lost with my research understanding this. The following works though, I just cannot explain... I must have a deep miss conception about something here.

public Config(String mode){
    final String path;
    if (mode = "1") {
        path = "<path1>";
    } else {
        path = "<path2>";
    }
    doSomething(path);
}

Can you explain me what is going on here, what should I read about to get this.

sanyassh
  • 8,100
  • 13
  • 36
  • 70
programmar
  • 594
  • 6
  • 19
  • 1
    To understand your second example you need to read up on variable scoping (which will also explain why your third example works) – JonK May 03 '19 at 08:05
  • What exactly you don't understand? – talex May 03 '19 at 08:07
  • `if (mode = "1") {` is invalid syntax: `mode = "1"` is a `String`, not a boolean. – Andy Turner May 03 '19 at 08:10
  • 2
    And `if (mode == "1")` would be wrong, too. You have to use the equals() method to compare strings. – GhostCat May 03 '19 at 08:11
  • @GhostCat it *may* be wrong :) It's *probably* wrong - just not definitely. – Andy Turner May 03 '19 at 08:12
  • 1
    Why do you want to make it final, what are you trying to achieve / prevent. – luk2302 May 03 '19 at 08:13
  • 2
    @luk2303 It is a good practice to make all variables `final` by default, and require a reason to make it re-assignable. – Thilo May 03 '19 at 08:14
  • 1
    @Thilo only when relevant. A final variable scoped in a constructor (or in any short-lived block) does not make much sense to be final. A field of a class is not the same as a local variable. Do you often see code with all local variables declared as final? I don't think so... – spi May 03 '19 at 08:16
  • 6
    @Thilo it's good practice to write code where variables are effectively final, and good practice to make members final whenever possible, but the additional visual noise of making *everything* final outweighs the benefit. – Andy Turner May 03 '19 at 08:19
  • @Thilo Or to say it in short: stating a universal rule to make all variables final is **not** at all a good practice. – GhostCat May 03 '19 at 09:08

3 Answers3

11

Can you explain me what is going on here,

Snippet 2: path declared in the scope of the if statement. It's not accessible outside that if.

Snippet 3: path declared in the scope of the constructor. It's accessible within that constructor.

what should I read about to get this.

The JLS, of course: https://docs.oracle.com/javase/specs/jls/se12/html/jls-6.html#jls-6.3 It's quite complicated, find the right part, read it thoughtfully and go with

doSomething("1".equals(mode) ? "<path1>" : "<path2>");
Andy Turner
  • 137,514
  • 11
  • 162
  • 243
Andrew Tobilko
  • 48,120
  • 14
  • 91
  • 142
3

The scope of any final variable lies within its code block. It is not visible outside this block. See this thread for why scope of final is defined this way: Scope of final local variable in java

harshrd
  • 79
  • 9
1

Thing is:

public Config(){
    final String path = "<path>";
    doSomething(path);
}

doesn't make any sense. doSomething() can not alter the String object you are passing to it anyway.

Your whole idea to use final like that is simply flawed. String objects are immutable, and that method receives a reference to such a String object. So even if you have

void doSomething(String whatever) {
  whatever = "in your face";
  ... 
}

Your path reference will still be the same after that call.

From that point of view, in your first example, you could (should for readability) go with doSomething("<path>");

Beyond that, the real answer here is: you seem to have misconceptions about proper usages of the final keyword. There is no point in declaring a variable final when it is used only once afterwards, like in your examples. Having a local variable final only prevents that this variable gets re-assigned within its scope. You are only reading the variable once, thus using final doesn't add anything useful to your code.

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • 1
    Who said that they were trying to alter the string inside `doSomething`? – JonK May 03 '19 at 08:08
  • the idea of declaring a variable is flawed as well, OP might have passed the literal – Andrew Tobilko May 03 '19 at 08:09
  • @JonK The example shows exactly **one** usage of that final variable. – GhostCat May 03 '19 at 08:09
  • @AndrewTobilko True, I enhanced the answer accordingly. – GhostCat May 03 '19 at 08:10
  • And? The body of `doSomething` is never shown, so how did you arrive at the conclusion that the problem is them expecting the content of the string to be changed as a side-effect? – JonK May 03 '19 at 08:10
  • @JonK I added another paragraph to make things more clear. – GhostCat May 03 '19 at 08:13
  • 1
    Still missing the point though - the OP actually has working code and is trying to understand why their second code snippet doesn't compile - which is due to the `path` variable being referenced after it has fallen out of scope. I agree that in the example code the use of `final` is functionally superfluous, but at the same time it doesn't actively harm the code either. Who knows, maybe their coding standards mandate that all variables are `final`. – JonK May 03 '19 at 08:17
  • @JonK Other answers do a nice job explaining that. I am answering to the question title that says "How to declare a final String inside a constructor with an if-statement?" ... and my answer is: in your current examples, you dont. Because it doesnt make any sense. – GhostCat May 03 '19 at 08:19
  • In fact, you can alter a String. It's a bit tricky and does not always work but it's doable on Java 8: [here is a snippet](https://ideone.com/YTe5cj). Note that having a final String does not prevent that :) – Abrikot May 03 '19 at 09:01
  • 4
    @Abrikot I know that Java has some corners for dark magic, but the newbie asking the question did start of with ` if (mode = "1")` so I did decide to not further complicate things by using reflection to get to String internal fields. And with Java 9+ and proper module support, that trick wont work anymore anyway ;-) – GhostCat May 03 '19 at 09:06