In Java, the variable name can be same with the classname - java

In Java I can declare a variable, whose name is total same with its classname. I think it is a so confusing and strange design.
So I have a problem in the code snippet below: how can the compiler distinguish the ClassName, it is referenced the variable name or class name?
In the running result, the compiler references ClassName as a variable name.
class ClassName{}
public class Test {
public static void main(String[] args){
ClassName ClassName = new ClassName();
System.out.println(ClassName); //ClassName#18fb53f6
}
}

The compiler can tell by context. In the example you have given:
ClassName ClassName = new ClassName();
1 2 3
It can see that 1 is where a type name should be, so it knows you mean the class. Then, 2 is where a variable name is expected, so it knows that this should be the name of a variable. And 3 is coming after the new keyword with parentheses, so it must be the name of a class.
System.out.println( ClassName );
In this instance, ClassName is in the context of argument passing. A type name can't be passed as an argument, so you must mean the name of the variable.
To amuse yourself, you can change the print statement to:
System.out.println( ClassName.class );
Hover your mouse cursor on ClassName and you'll see that the compiler recognizes this as the name of a class. Then change it to:
System.out.println( ClassName.getClass() );
Hover your cursor again, and now you see that it recognizes it as the variable name. That's because .class can only be applied to a type name, while getClass() can only be applied to an object reference. The result of the print statement would be the same in both cases - but through different mechanisms.
So the compiler has no problem here. But you are right that it's not readable to humans. The convention is that names of variables and methods must start with a lowercase letter, while type names must start with an uppercase letter. Adhering to this convention will ensure that no such readability problems arise.
I can't say exactly why the authors of Java chose not to enforce this convention (that is, give a compiler error if type names started with a lowercase letter or variable/method names started with an uppercase), but I speculate that they didn't want to make anything an actual error unless it would actually cause an ambiguity for the compiler. Compilation errors are supposed to indicate a problem that makes the compiler unable to do its work.

how can the compiler distinguish the "Classname"
Because there are two components: The variable type and variable name. You declare a variable ClassName of type ClassName. Type always goes first. Classes are not first-class objects (meaning you can't have a reference to a class) unless you get into reflections (with the .class property).
Therefore, in the print statement:
System.out.println(ClassName);
That can only be the variable. System.out.println takes an object reference, and you have an object referred to by a variable named ClassName, therefore the compiler can resolve it.
The only case I can think that is ambiguous to the compiler is if the variable refers to an object which has an instance method of the same name as a static method on the class.
public class SomeClass {
public void aMethod() {
System.out.println("A method!");
}
public static void aMethod() {
System.out.println("Static version!");
}
}
public class TestClass {
public static void main (String[] args) {
SomeClass SomeClass = new SomeClass();
SomeClass.aMethod(); // does this call the instance method or the static method?
}
}
I am sure the compiler will detect the ambiguity and handle it in some specified manner (in the Java spec). Probably one of:
Don't allow a static and instance method to have the same name.
Allow it, and when resolving the reference at compile-time, prefer the instance method.
Allow it, and when resolving the reference at compile-time, prefer the static method.
If either of the last 2, I imagine a compiler warning would be logged.
Now that the compiler question is aside, the only other consumer of the code is human beings. Compilers may be able to rely on specifications to guarantee rationale behavior, but humans can't. We get confused easily. The best advice I have for that is simply, don't do it!
There is absolutely no reason to name a variable identically to a class. In fact, most Java coding style conventions I have seen use lowerCamelCase to name variables and methods and UpperCamelCase to name classes, so there is no way for them to collide unless you deviated from the standards.
If I encountered code like that in a project I was working on, I would immediately rename the variable before doing anything else.
For my ambiguous case of an instance and static method of the same name, there just might be a human lesson in there too: don't do it!
Java has a lot of rules to force you to do things that are logical and make code easy to follow, but at the end of the day, it's still code and you can write any code you want. No language spec or compiler can prevent you from writing confusing code.

ClassName ClassName = new ClassName();
If you study compiler design course, you will know there is a Lexical Analysis step. At this step, you will write a grammar for your language. for example:
ClassName variableName = new ClassName();
So example above, compiler can understand second ClassName is variable.
When you do something like:
ClassName.doSomething();
Java will understand ClassName as variable rather than a class. And this design won't have any limitation. doSomething() can be both static method or just a instance method.
If Java understands ClassName here as class, so doSomething() cannot be a instance method. Maybe because this so Java creator has chosen above design: ClassName as a variable.
But what the problem if a variable name cannot be same name with their class. so the following example:
ClassA ClassB = new ClassA();
ClassB.callMethodInClassB(); // should compile error or not ???!!!
The problem still be here. The misleading still exist. So the new design should be:
No variable name should not has same name with **any** class name.
And you will see, this statement makes one language more complicate to understand and not so well-define. From above proofs, I think when you do something such as: A A = new A(); understand A as variable is a best way in language design.
Hope this help :)

Related

What does $$ in javac generated name mean?

When shifting through java call graph generated by libraries like DependencyFinder and java-callgraph, I found out that java compiler generate names for anonymous functions, inner classes, etc.
I've found out the meaning of a couple of them (please correct if I'm wrong):
org.example.Bar$Foo refers to Foo, which is an inner class of org.example.Bar.
org.example.Bar$1 refers to an anonymous class declared inside one of the methods of org.example.Bar.
org.example.Bar.lambda$spam$1() refers to a lambda declared inside of org.example.Bar.spam() method.
However, I also found:
org.example.Bar$$Lambda$2.args$1
org.example.Bar$$Lambda$2.call()
org.example.Bar$$Lambda$7.lambdaFactory$()
org.example.Bar$$Lambda$7.get$Lambda()
What does the four name above refer to? What does double dollar ($$) mean?
The classes for lambda expressions are not javac generated, but created at runtime by the JRE. Their names are completely unspecified and you can’t rely on any naming scheme.
But obviously, Oracle’s current JRE has a recognizable pattern. It appends $$Lambda$n to the name of the defining class whereas n is an increasing number, which reflects the creation order at runtime, rather than any property of the compiled code.
You can verify this with the following program:
public class Test {
public static void main(String... args) {
if(args.length==0) {
final boolean meFirst = Math.random()<0.5;
if(meFirst) {
Runnable r=Test::main;
System.out.println("first run:\t"+r.getClass());
}
main("second run");
if(!meFirst) {
Runnable r=Test::main;
System.out.println("first run:\t"+r.getClass());
}
}
else {
Runnable r=Test::main;
System.out.println(args[0]+":\t"+r.getClass());
if(args[0].equals("second run")) main("last run");
}
}
}
Depending on the state of the random meFirst flag, it will print either
first run: class Test$$Lambda$1
second run: class Test$$Lambda$2
last run: class Test$$Lambda$2
or
second run: class Test$$Lambda$1
last run: class Test$$Lambda$1
first run: class Test$$Lambda$2
It shows that the first generated class always gets the number 1, regardless of whether it’s one of the first two method references instantiated in the first main invocation or the third method reference, instantiated in the first recursion. Further, the 3rd execution always encounters the same class as the 2nd, as it’s the same method reference expression (note: distinct expression, as the target of all expressions is the same) and the class is re-used.
Depending on the version, you may further see something like /number appended to the names, which hints that the names really don’t matter, as each of these classes has another unique identifier (they are so called “anonymous classes” which you can’t locate via ClassLoader and name).
Field names like args$n within these classes represent the n’th captured value. Since the LambdaMetafactory has no knowledge about the actual names of the captured variables, it has no other choice but to generate such names.
But as said, that’s an implementation artifact. It’s possible to maintain such a naming pattern, as long as a new class is generated for each creation site in each defining class. But since the specification allows arbitrary sharing/reusing of classes and instances representing equivalent lambda expressions (doing the same) and method references (targeting the same method), such a naming pattern is not possible with every implementation strategy.

How to fully qualify a class whose package name collides with a local member name?

OK, here's a very curious Java 7 language puzzle for the JLS specialists out there. The following piece of code won't compile, neither with javac nor with Eclipse:
package com.example;
public class X {
public static X com = new X();
public void x() {
System.out.println(com.example.X.com);
// cannot find symbol ^^^^^^^
}
}
It appears as though the member com completely prevents access to the com.* packages from within X. This isn't thoroughly applied, however. The following works, for instance:
public void x() {
System.out.println(com.example.X.class);
}
My question(s):
How is this behaviour justified from the JLS?
How can I work around this issue
Note, this is just a simplification for a real problem in generated code, where full qualification of com.example.X is needed and the com member cannot be renamed.
Update: I think it may actually be a similar problem like this one: Why can't I "static import" an "equals" method in Java?
This is called obscuring (jls-6.4.2).
A simple name may occur in contexts where it may potentially be
interpreted as the name of a variable, a type, or a package. In these
situations, the rules of §6.5 specify that a variable will be chosen
in preference to a type, and that a type will be chosen in preference
to a package. Thus, it is may sometimes be impossible to refer to a
visible type or package declaration via its simple name. We say that
such a declaration is obscured.
Your attribute com.example.X.com is not static so it can't be accessed via your X class in a static way. You can access it only via an instance of X.
More than that, each time you will instanciate an X, it will lead to a new X : I can predict a memory explosion here.
Very bad code :)
How can I work around this issue?
Using a fully qualified class name here can be a problem because, in general, package names and variable names both start with lower case letters and thus can collide. But, you do not need to use a fully qualified class name to gain a reference a class's static member; you can reference it qualified just by the class name. Since class names should start with an upper case character, they should never collide with a package name or variable. (And you can import an arbitrary class with its fully qualified class name without issue, because the import statement will never confuse a variable name for a package name.)
public void x() {
// System.out.println(com.example.X.com);
// cannot find symbol ^^^^^^^
System.out.println(X.com); // Works fine
}

Java object construction

I am new to Java and I'd like to figure out why some code is written in one way instead of another. When you construct an object in Java, the syntax is something like
class variable = new class(parameters);
or
class variable;
variable = new class(parameters);
I wonder why the class name has to be invoked twice. As documented on Wikipedia, Python (which I don't know either) follows what according to me is a more intuitive approach, i.e.
variable = class(parameters)
Is it because Java can handle other possibilities?, e.g.
class1 variable = ... class2(parameters)
Thanks in advance.
Because the left hand side can be a reference type and the right hand side it can be any assignable object type.
This assigns the reference variable variable of type class1 an object of class1.
class1 variable = new class1(parameters);
This assigns the reference variable variable of type class1 an object of subclass1, where subclass1 can be a subclass of class1 or an implementation of class1 interface.
class1 variable = new subclass1(parameters);
Python uses Dynamic Typing (Duck Typing) and hence you need not declare a variable.Dynamic typed languages are those in which variable type checking is done at run-time.
Static typed programming languages are those in which variables need not be defined before they’re used. This implies that static typing has to do with the explicit declaration (or initialization) of variables before they’re employed. Java is an example of a static typed language. Statically typed languages that lack type inference (such as C and Java) require that programmers declare the types they intend a method or function to use. This can serve as additional documentation for the program, which the compiler will not permit the programmer to ignore or permit to drift out of synchronization.Static typed languages are those in which variable type checking is done at compile-time.
Python (which I don't know either) follows what according to me is a more intuitive approach
The concern with dynamic typing, like this is :
variable = class(parameters);
In future if by mistake you misspelled the variable name variable with varaible , it would not be caught at compile time , but you can get erroneous output or exception at runtime. With static typing , Java detects such flaws at compile time itself.
Because they mean different things - and don't need to be the same.
The class descriptor on the left defines the type of the variable. This constrains what can be assigned to that variable, and also provides some guarantees to callers about what the objects there can do.
The class name when it appears on the right is actually the "method name" of a constructor. You're calling a method which creates a new object. The object you create can be anything, so long as it's assignable to the type of the variable.
So for example, you can do this:
Object foo = new String("bar");
or this:
Collection x = new ArrayList();
(though in practice you'd probably want to use generic parameters on that last one - I've left them out here so as not to confuse the question of classes.)
Java is a static typed programming language. However, Python is a dynamic typed programming language.
Wikipedia has very good explanation here.
Class name has to be invoked twice because you have to specify the type of the instance.
You can also do things like this when creating new instance:
public class Test extends Test1 {}
...
Test1 test = new Test(parameters);
Doing things like this will determine which class' variables and methods you can actually access while maintaining stuffs such as class fields.
SomeClass variable;
variable = new SomeClass(params);
The first line declares a variable and states that it is of type SomeClass. At this point the variable is empty and doesn't reference anything.
The second line creates a new object of type SomeClass and stores a reference to it in variable.
Writing both things in one line like this:
SomeClass variable = new SomeClass(params);
can be done to make things shorter. But at the end there is always a variable declaration and an object construction. Variables must always be of some type, you can't declare a variable without type (Java is strongly typed).
The left hand side (reference definition type) is a reference to an object or instance that can be the type of that class or any subclass of that reference type. Also, in accord with the following reference Python is a dynamic type programming language where every variable name (unless it's null) is bound to only an object. Java, on the other hand, is a statically-typed programming language.

Java static imports

Just by experiment I discovered that Java non static methods overrides all same named methods in scope even at static context. Even without allowing parameter overloading. Like
import java.util.Arrays;
import static java.util.Arrays.toString;
public class A {
public static void bar(Object... args) {
Arrays.toString(args);
toString(args); //toString() in java.lang.Object cannot be applied to (java.lang.Object[])
}
}
I can't find anything about this in spec. Is this a bug? If it isn't, are there any reasons to implement language like that?
UPD: Java 6 do not compile this example. The question is - why?
The explanation is simple although it doesn't change the fact that the behavior is highly unintuitive:
When resolving the method to be invoked the first thing the compiler does is find the smallest enclosing scope that has a method of the right name. Only then come other things like overload resolution and co in game.
Now what is happening here is that the smallest enclosing scope that contains a toString() method is class A which inherits it from Object. Hence we stop there and don't search farther. Sadly next the compiler tries to find the best fit of the methods in the given scope and notices that it can't call any of those and gives an error.
Which means never statically import methods with a name that is identical to a method in Object, because methods that are naturally in scope take precedence over static imports (the JLS describes method shadowing in detail, but for this problem I think it's much simpler to just remember that).
Edit: #alf kindly submitted the right part of the JLS that describes the method invocation for those who want the whole picture. It's rather complex, but then the problem isn't simple either so that's to be expected.
It is not an override. If it did work, this.toString() would still access the method of A instead of Arrays.toString as would be the case if overriding had occurred.
The language specification explains that static imports only affect the resolution of static methods and types:
A single-static-import declaration d in a compilation unit c of package p that imports a field named n shadows the declaration of any static field named n imported by a static-import-on-demand declaration in c, throughout c.
A single-static-import declaration d in a compilation unit c of package p that imports a method named n with signature s shadows the declaration of any static method named n with signature s imported by a static-import-on-demand declaration in c, throughout c.
A single-static-import declaration d in a compilation unit c of package p that imports a type named n shadows the declarations of:
any static type named n imported by a static-import-on-demand declaration in c.
any top level type (§7.6) named n declared in another compilation unit (§7.3) of p.
any type named n imported by a type-import-on-demand declaration (§7.5.2) in c.
throughout c.
Static imports do not shadow non-static methods or inner types.
So the toString does not shadow the non-static method. Since the name toString can refer to a non-static method of A, it does cannot refer to the static method of Arrays and thus toString binds to the only method named toString that is available in scope, which is String toString(). That method cannot take any arguments so you get a compile error.
Section 15.12.1 explains method resolution and would have to have been completely rewritten to allow shadowing of unavailable method names within static methods but not inside member methods.
My guess is that the language designers wanted to keep method resolution rules simple, which means that the same name means the same thing whether it appears in a static method or not, and the only thing that changes is which are available.
If you try following similar looking code then you will not get any compiler error
import static java.util.Arrays.sort;
public class StaticImport {
public void bar(int... args) {
sort(args); // will call Array.sort
}
}
The reason this compiles and yours doesn't is that the toString() (or any other method defined in class Object) are still scoped to Object class because of Object being the parent of your class. Hence when compiler finds matching signature of those methods from Object class then it gives compiler error. In my example since Object class doesn't have sort(int[]) method hence compiler rightly matches it with the static import.
I do not think it is a bug or something different from normal import. For example, in case of normal import, if you have a private class with the same name as imported one the imported one will not be reflected.

Why should I use the keyword "final" on a method parameter in Java?

I can't understand where the final keyword is really handy when it is used on method parameters.
If we exclude the usage of anonymous classes, readability and intent declaration then it seems almost worthless to me.
Enforcing that some data remains constant is not as strong as it seems.
If the parameter is a primitive then it will have no effect since the parameter is passed to the method as a value and changing it will have no effect outside the scope.
If we are passing a parameter by reference, then the reference itself is a local variable and if the reference is changed from within the method, that would not have any effect from outside of the method scope.
Consider the simple test example below.
This test passes although the method changed the value of the reference given to it, it has no effect.
public void testNullify() {
Collection<Integer> c = new ArrayList<Integer>();
nullify(c);
assertNotNull(c);
final Collection<Integer> c1 = c;
assertTrue(c1.equals(c));
change(c);
assertTrue(c1.equals(c));
}
private void change(Collection<Integer> c) {
c = new ArrayList<Integer>();
}
public void nullify(Collection<?> t) {
t = null;
}
Stop a Variable’s Reassignment
While these answers are intellectually interesting, I've not read the short simple answer:
Use the keyword final when you want the compiler to prevent a
variable from being re-assigned to a different object.
Whether the variable is a static variable, member variable, local variable, or argument/parameter variable, the effect is entirely the same.
Example
Let’s see the effect in action.
Consider this simple method, where the two variables (arg and x) can both be re-assigned different objects.
// Example use of this method:
// this.doSomething( "tiger" );
void doSomething( String arg ) {
String x = arg; // Both variables now point to the same String object.
x = "elephant"; // This variable now points to a different String object.
arg = "giraffe"; // Ditto. Now neither variable points to the original passed String.
}
Mark the local variable as final. This results in a compiler error.
void doSomething( String arg ) {
final String x = arg; // Mark variable as 'final'.
x = "elephant"; // Compiler error: The final local variable x cannot be assigned.
arg = "giraffe";
}
Instead, let’s mark the parameter variable as final. This too results in a compiler error.
void doSomething( final String arg ) { // Mark argument as 'final'.
String x = arg;
x = "elephant";
arg = "giraffe"; // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
}
Moral of the story:
If you want to ensure a variable always points to the same object,
mark the variable final.
Never Reassign Arguments
As good programming practice (in any language), you should never re-assign a parameter/argument variable to an object other than the object passed by the calling method. In the examples above, one should never write the line arg = . Since humans make mistakes, and programmers are human, let’s ask the compiler to assist us. Mark every parameter/argument variable as 'final' so that the compiler may find and flag any such re-assignments.
In Retrospect
As noted in other answers…
Given Java's original design goal of helping programmers to avoid dumb mistakes such as reading past the end of an array, Java should have been designed to automatically enforce all parameter/argument variables as 'final'. In other words, Arguments should not be variables. But hindsight is 20/20 vision, and the Java designers had their hands full at the time.
So, always add final to all arguments?
Should we add final to each and every method parameter being declared?
In theory, yes.
In practice, no.➥ Add final only when the method’s code is long or complicated, where the argument may be mistaken for a local or member variable and possibly re-assigned.
If you buy into the practice of never re-assigning an argument, you will be inclined to add a final to each. But this is tedious and makes the declaration a bit harder to read.
For short simple code where the argument is obviously an argument, and not a local variable nor a member variable, I do not bother adding the final. If the code is quite obvious, with no chance of me nor any other programmer doing maintenance or refactoring accidentally mistaking the argument variable as something other than an argument, then don’t bother. In my own work, I add final only in longer or more involved code where an argument might mistaken for a local or member variable.
#Another case added for the completeness
public class MyClass {
private int x;
//getters and setters
}
void doSomething( final MyClass arg ) { // Mark argument as 'final'.
arg = new MyClass(); // Compiler error: The passed argument variable arg cannot be re-assigned to another object.
arg.setX(20); // allowed
// We can re-assign properties of argument which is marked as final
}
record
Java 16 brings the new records feature. A record is a very brief way to define a class whose central purpose is to merely carry data, immutably and transparently.
You simply declare the class name along with the names and types of its member fields. The compiler implicitly provides the constructor, getters, equals & hashCode, and toString.
The fields are read-only, with no setters. So a record is one case where there is no need to mark the arguments final. They are already effectively final. Indeed, the compiler forbids using final when declaring the fields of a record.
public record Employee( String name , LocalDate whenHired ) // 🡄 Marking `final` here is *not* allowed.
{
}
If you provide an optional constructor, there you can mark final.
public record Employee(String name , LocalDate whenHired) // 🡄 Marking `final` here is *not* allowed.
{
public Employee ( final String name , final LocalDate whenHired ) // 🡄 Marking `final` here *is* allowed.
{
this.name = name;
whenHired = LocalDate.MIN; // 🡄 Compiler error, because of `final`.
this.whenHired = whenHired;
}
}
Sometimes it's nice to be explicit (for readability) that the variable doesn't change. Here's a simple example where using final can save some possible headaches:
public void setTest(String test) {
test = test;
}
If you forget the 'this' keyword on a setter, then the variable you want to set doesn't get set. However, if you used the final keyword on the parameter, then the bug would be caught at compile time.
Yes, excluding anonymous classes, readability and intent declaration it's almost worthless. Are those three things worthless though?
Personally I tend not to use final for local variables and parameters unless I'm using the variable in an anonymous inner class, but I can certainly see the point of those who want to make it clear that the parameter value itself won't change (even if the object it refers to changes its contents). For those who find that adds to readability, I think it's an entirely reasonable thing to do.
Your point would be more important if anyone were actually claiming that it did keep data constant in a way that it doesn't - but I can't remember seeing any such claims. Are you suggesting there's a significant body of developers suggesting that final has more effect than it really does?
EDIT: I should really have summed all of this up with a Monty Python reference; the question seems somewhat similar to asking "What have the Romans ever done for us?"
Let me explain a bit about the one case where you have to use final, which Jon already mentioned:
If you create an anonymous inner class in your method and use a local variable (such as a method parameter) inside that class, then the compiler forces you to make the parameter final:
public Iterator<Integer> createIntegerIterator(final int from, final int to)
{
return new Iterator<Integer>(){
int index = from;
public Integer next()
{
return index++;
}
public boolean hasNext()
{
return index <= to;
}
// remove method omitted
};
}
Here the from and to parameters need to be final so they can be used inside the anonymous class.
The reason for that requirement is this: Local variables live on the stack, therefore they exist only while the method is executed. However, the anonymous class instance is returned from the method, so it may live for much longer. You can't preserve the stack, because it is needed for subsequent method calls.
So what Java does instead is to put copies of those local variables as hidden instance variables into the anonymous class (you can see them if you examine the byte code). But if they were not final, one might expect the anonymous class and the method seeing changes the other one makes to the variable. In order to maintain the illusion that there is only one variable rather than two copies, it has to be final.
I use final all the time on parameters.
Does it add that much? Not really.
Would I turn it off? No.
The reason: I found 3 bugs where people had written sloppy code and failed to set a member variable in accessors. All bugs proved difficult to find.
I'd like to see this made the default in a future version of Java. The pass by value/reference thing trips up an awful lot of junior programmers.
One more thing.. my methods tend to have a low number of parameters so the extra text on a method declaration isn't an issue.
Using final in a method parameter has nothing to do with what happens to the argument on the caller side. It is only meant to mark it as not changing inside that method. As I try to adopt a more functional programming style, I kind of see the value in that.
Personally I don't use final on method parameters, because it adds too much clutter to parameter lists.
I prefer to enforce that method parameters are not changed through something like Checkstyle.
For local variables I use final whenever possible, I even let Eclipse do that automatically in my setup for personal projects.
I would certainly like something stronger like C/C++ const.
Since Java passes copies of arguments I feel the relevance of final is rather limited. I guess the habit comes from the C++ era where you could prohibit reference content from being changed by doing a const char const *. I feel this kind of stuff makes you believe the developer is inherently stupid as f*** and needs to be protected against truly every character he types. In all humbleness may I say, I write very few bugs even though I omit final (unless I don't want someone to override my methods and classes). Maybe I'm just an old-school dev.
Short answer: final helps a tiny bit but... use defensive programming on the client side instead.
Indeed, the problem with final is that it only enforces the reference is unchanged, gleefully allowing the referenced object members to be mutated, unbeknownst to the caller. Hence the best practice in this regard is defensive programming on the caller side, creating deeply immutable instances or deep copies of objects that are in danger of being mugged by unscrupulous APIs.
I never use final in a parameter list, it just adds clutter like previous respondents have said. Also in Eclipse you can set parameter assignment to generate an error so using final in a parameter list seems pretty redundant to me.
Interestingly when I enabled the Eclipse setting for parameter assignment generating an error on it caught this code (this is just how I remember the flow, not the actual code. ) :-
private String getString(String A, int i, String B, String C)
{
if (i > 0)
A += B;
if (i > 100)
A += C;
return A;
}
Playing devil's advocate, what exactly is wrong with doing this?
One additional reason to add final to parameter declarations is that it helps to identify variables that need to be renamed as part of a "Extract Method" refactoring. I have found that adding final to each parameter prior to starting a large method refactoring quickly tells me if there are any issues I need to address before continuing.
However, I generally remove them as superfluous at the end of the refactoring.
Follow up by Michel's post. I made myself another example to explain it. I hope it could help.
public static void main(String[] args){
MyParam myParam = thisIsWhy(new MyObj());
myParam.setArgNewName();
System.out.println(myParam.showObjName());
}
public static MyParam thisIsWhy(final MyObj obj){
MyParam myParam = new MyParam() {
#Override
public void setArgNewName() {
obj.name = "afterSet";
}
#Override
public String showObjName(){
return obj.name;
}
};
return myParam;
}
public static class MyObj{
String name = "beforeSet";
public MyObj() {
}
}
public abstract static class MyParam{
public abstract void setArgNewName();
public abstract String showObjName();
}
From the code above, in the method thisIsWhy(), we actually didn't assign the [argument MyObj obj] to a real reference in MyParam. In instead, we just use the [argument MyObj obj] in the method inside MyParam.
But after we finish the method thisIsWhy(), should the argument(object) MyObj still exist?
Seems like it should, because we can see in main we still call the method showObjName() and it needs to reach obj. MyParam will still use/reaches the method argument even the method already returned!
How Java really achieve this is to generate a copy also is a hidden reference of the argument MyObj obj inside the MyParam object ( but it's not a formal field in MyParam so that we can't see it )
As we call "showObjName", it will use that reference to get the corresponding value.
But if we didn't put the argument final, which leads a situation we can reassign a new memory(object) to the argument MyObj obj.
Technically there's no clash at all! If we are allowed to do that, below will be the situation:
We now have a hidden [MyObj obj] point to a [Memory A in heap] now live in MyParam object.
We also have another [MyObj obj] which is the argument point to a [Memory B in heap] now live in thisIsWhy method.
No clash, but "CONFUSING!!" Because they are all using the same "reference name" which is "obj".
To avoid this, set it as "final" to avoid programmer do the "mistake-prone" code.

Categories

Resources