I want to know about the difference between Parameters, Arguments & Local Variables in JAVA. Can anyone explain me about them very well?
There is an unclear point in the following code for me. This is a part of a Java code.
Language(String t) {
name = t;
}
and.....
Language() {
String t;
name = t;
}
I want to know the difference between above two types. What is the difference? What is the difference when running the code?
In addition to the fact that as commenters pointed out above your second code snippet cannot compile, parameters and arguments are essentially the same thing (t in your first example). They are variables passed to a method.
A local variable is a variable declared within a method, so in a fixed snippet:
public void myMethod(string t) {
int x = 6;
String y = t;
}
Here t is an argument (or parameter). y is a local variable.
The difference between your two examples is that in the first example:
Language(String t) {
name = t;
}
this example takes a parameter in the constructor (assuming Language() is a constructor by common naming conventions. A parameter is also called an argument. It's a value that is passed in from outside when this method/Constructor is called. 'name' is not defined anywhere in this example, so as it is, this code won't compile.
In your second example:
Language() {
String t;
name = t;
}
there is no argument/parameter defined
't' is a local variable. Local means it's local within the scope of this block { } and therefore only has visibility inside this block
name again is not defined anywhere, so will not compile
't' is never assigned a value and so is null
name if it was defined somewhere would be assigned null
The main difference therefore is that the first example takes a parameter and attempts to assign it to undefined 'name', whereas the second example does not take a parameter.
Related
This is meant to be a canonical question and answer for similar questions where the issue is a result of shadowing.
I've defined two fields in my class, one of a reference type and one of a primitive type. In the class' constructor, I try to initialize them to some custom values.
When I later query for those fields' values, they come back with Java's default values for them, null for the reference type and 0 for the primitive type. Why is this happening?
Here's a reproducible example:
public class Sample {
public static void main(String[] args) throws Exception {
StringArray array = new StringArray();
System.out.println(array.getCapacity()); // prints 0
System.out.println(array.getElements()); // prints null
}
}
class StringArray {
private String[] elements;
private int capacity;
public StringArray() {
int capacity = 10;
String[] elements;
elements = new String[capacity];
}
public int getCapacity() {
return capacity;
}
public String[] getElements() {
return elements;
}
}
I expected getCapacity() to return the value 10 and getElements() to return a properly initialized array instance.
Entities (packages, types, methods, variables, etc.) defined in a Java program have names. These are used to refer to those entities in other parts of a program.
The Java language defines a scope for each name
The scope of a declaration is the region of the program within which
the entity declared by the declaration can be referred to using a
simple name, provided it is visible (§6.4.1).
In other words, scope is a compile time concept that determines where a name can be used to refer to some program entity.
The program you've posted has multiple declarations. Let's start with
private String[] elements;
private int capacity;
These are field declarations, also called instance variables, ie. a type of member declared in a class body. The Java Language Specification states
The scope of a declaration of a member m declared in or inherited by a
class type C (§8.1.6) is the entire body of C, including any nested
type declarations.
This means you can use the names elements and capacity within the body of StringArray to refer to those fields.
The two first statements in your constructor body
public StringArray() {
int capacity = 10;
String[] elements;
elements = new String[capacity];
}
are actually local variable declaration statements
A local variable declaration statement declares one or more local variable names.
Those two statements introduce two new names in your program. It just so happens that those names are the same as your fields'. In your example, the local variable declaration for capacity also contains an initializer which initializes that local variable, not the field of the same name. Your field named capacity is initialized to the default value for its type, ie. the value 0.
The case for elements is a little different. The local variable declaration statement introduces a new name, but what about the assignment expression?
elements = new String[capacity];
What entity is elements referring to?
The rules of scope state
The scope of a local variable declaration in a block (§14.4) is the
rest of the block in which the declaration appears, starting with its
own initializer and including any further declarators to the right in
the local variable declaration statement.
The block, in this case, is the constructor body. But the constructor body is part of the body of StringArray, which means field names are also in scope. So how does Java determine what you're referring to?
Java introduces the concept of Shadowing to disambiguate.
Some declarations may be shadowed in part of their scope by another
declaration of the same name, in which case a simple name cannot be
used to refer to the declared entity.
(a simple name being a single identifier, eg. elements.)
The documentation also states
A declaration d of a local variable or exception parameter named n
shadows, throughout the scope of d, (a) the declarations of any other
fields named n that are in scope at the point where d occurs, and (b)
the declarations of any other variables named n that are in scope at
the point where d occurs but are not declared in the innermost class
in which d is declared.
This means that the local variable named elements takes priority over the field named elements. The expression
elements = new String[capacity];
is therefore initializing the local variable, not the field. The field is initialized to the default value for its type, ie. the value null.
Inside your methods getCapacity and getElements, the names you use in the in their respective return statements refer to the fields since their declarations are the only ones in scope at that particular point in the program. Since the fields were initialized to 0 and null, those are the values returned.
The solution is to get rid of the local variable declarations altogether and therefore have the names refer to the instance variables, as you originally wanted. For example
public StringArray() {
capacity = 10;
elements = new String[capacity];
}
Shadowing with constructor parameters
Similar to the situation described above, you may have formal (constructor or method) parameters shadowing fields with the same name. For example
public StringArray(int capacity) {
capacity = 10;
}
Shadowing rules state
A declaration d of a field or formal parameter named n shadows,
throughout the scope of d, the declarations of any other variables
named n that are in scope at the point where d occurs.
In the example above, the declaration of the constructor parameter capacity shadows the declaration of the instance variable also named capacity. It's therefore impossible to refer to the instance variable with its simple name. In such cases, we need to refer to it with its qualified name.
A qualified name consists of a name, a "." token, and an identifier.
In this case, we can use the primary expression this as part of a field access expression to refer to the instance variable. For example
public StringArray(int capacity) {
this.capacity = 10; // to initialize the field with the value 10
// or
this.capacity = capacity; // to initialize the field with the value of the constructor argument
}
There are Shadowing rules for every kind of variable, method, and type.
My recommendation is that you use unique names wherever possible so as to avoid the behavior altogether.
int capacity = 10; in your constructor is declaring an local variable capacity which shadows the field of the class.
The remedy is to drop the int:
capacity = 10;
This will change the field value. Ditto for the other field in the class.
Didn't your IDE warn you of this shadowing?
There are two parts to using variables in java/c/c++. One is to declare the variable and the other is to use the variable (whether assigning a value or using it in a calculation).
When you declare a variable you must declare its type. So you would use
int x; // to declare the variable
x = 7; // to set its value
You do not have to re-declare a variable when using it:
int x;
int x = 7;
if the variable is in the same scope you will get a compiler error; however, as you are finding out, if the variable is in a different scope you will mask the first declaration.
Another widely accepted convention is to have some prefix (or suffix - whatever you prefer) added to class members to distinguish them from local variables.
For example class members with m_ prefix:
class StringArray {
private String[] m_elements;
private int m_capacity;
public StringArray(int capacity) {
m_capacity = capacity;
m_elements = new String[capacity];
}
public int getCapacity() {
return m_capacity;
}
public String[] getElements() {
return m_elements;
}
}
Most IDEs have already available support for this notation, below is for Eclipse
new to the community, and new to the whole programming world.
While I was studying java, I stumbled on a simple question.
In a main method (or any method), I can declare and initialize a primitive variable on different line just fine. Say,
public static void main (Strin[]args){
int age;
age = 42;
}
will complile just fine.
But if I tried this outside a method, as a class variable or instance variable,
public class test {
int age;
age = 42;
}
the code won't compile. It will only work if the variable is declared and initialized in one line. I was wondering why java doesn't allow this outside a method.
A class body can contain variable declarations and method declarations, but no single statements. When would you expect such a statement to be executed? So your initialization has to be either inline with the declaration (as a shortcut) or in some method, e.g. in the constructor, if you want to initialize the variable when creating a new object.
It is a syntax error! Your code does not comply with the Java syntactic and semantics rules as described in Java Language Specification.
You have to initialise it's value inside the constructor (that's the whole point of a constructor), like
public test() {
age = 42;
}
For static variables it's possible to give them a value:
static int age = 42;
Or use a static block:
static {
age = 43;
}
There are several similar questions on SO about method reference to local class constructor, but I'd like to clarify slightly other thing. Consider following piece of code:
static Callable gen(int i) {
class X {
int x = i;
public String toString() { return "" + x; }
}
return X::new;
}
...
System.out.println(gen(0).call());
System.out.println(gen(1).call());
Obviously this will printout
0
1
It turns out, that X class has constructor of the form ...$X(int) (you can find it via X.class.getDeclaredConstructors()).
But what is interesting here, is that returned lambdas (or method references) aren't simple reference to constructor ...$X(int) like, for example, Integer::new. They internally invoke this constructor ...$X(int) with predefined argument (0 or 1).
So, I'm not sure, but looks like this kind of method reference is not precisely described in JLS. And there is not other way except this case for local classes, to produce such kind of lambdas (with predefined constructor arguments). Who can help clarify this?
To be precise:
where is in JLS such kind of method reference described?
is any other way to create such method reference to arbitrary class constructor with predefined arguments?
You are focusing too much on irrelevant low level details. On the byte code level, there might be a constructor accepting an int parameter, but on the language level, you didn’t specify an explicit constructor, hence, there will be a default constructor without any arguments, as with any other class.
This should become clear when you write the pre-Java 8 code:
static Callable<Object> gen(int i) {
class X {
int x = i;
public String toString() { return "" + x; }
}
X x=new X();
…
You instantiate X by its default constructor, not taking any arguments. Your local class captures the value of i, but how it does so on the low level, i.e. that X’ constructor has a synthetic int parameter and the new expression will pass the value of i to it, is an implementation detail.
You can even add an explicit constructor as
X() {}
without changing anything.
Obviously, you can also write the expression new X() inside a lambda expression here, as expressions don’t change their semantic when being placed inside a lambda expression:
return () -> new X();
or use it’s short-hand form, the method reference
return X::new;
There is nothing special about it, the behavior is understandable even without referring to the specification, if you forget about the distracting low level details. X may capture as many local variables as you like, the constructor’s number of parameters doesn’t change (on the language level).
This behaviour is defined in the JLS section §15.13.3:
If the form is ClassType :: [TypeArguments] new, the body of the invocation method has the effect of a class instance creation expression of the form new [TypeArguments] ClassType(A1, ..., An), where the arguments A1, ..., An are the formal parameters of the invocation method, and where:
The enclosing instance for the new object, if any, is derived from the site of the method reference expression, as specified in §15.9.2.
The constructor to invoke is the constructor that corresponds to the compile-time declaration of the method reference (§15.13.1).
Although this talks about enclosing instances, captured variables and parameters are not mentioned in §15.13.3.
As for your second question, you need to manually capture and change the parameter:
static Callable gen(int i) {
final int i1 = someCondition() ? i : 42;
class X {
int x = i1; // <-
public String toString() { return "" + x; }
}
return X::new;
}
Q: Can I change the declaration type for a variable in Java?
For e.g.,
public class Tmp{
public static void main(String[] args) {
String s = "Foo";
s = null; //same Error results whether this line included or not
int s = 3;
System.out.println(s);
}
}
But attempted compilation results in the message:
Error: variable s is already defined in method main(java.lang.String[])
Oddly, re-declaring the type of a variable works just fine in an interactive DrJava session:
> String s = "Foo"
> int s = 1
> s
1
What's going on?
Can I change the declaration type for a variable in Java?
No, the compiler knows that s already exists within the same scope and is declared of type String.
I've never used DrJava before but as an interactive interpreter, it may be able to de-scope the first variable and replace it with the one declared in the new statement.
Variable names inside a scope is fixed, so you cannot have same variable with multiple types. You can have same name with two different type but in a different scope. So below example if you consider is fine since we are changing type in two different scope. One instance level and second time method level.
public class Test {
private String variable = "";
private void init() {
int variable = 10;
}
}
No.
But you can try something like this
public class Tmp
{
public static void main(String[] args)
{
{
String s = "Foo";
s = null;
}
int s = 3;
System.out.println(s);
}
}
But do you really want this? It can be really confusing for the readers, if the type of a variable changes.
You can not change the declaration of a variable within the same scope.
Since everything in Java is an Object, you can as well declare s as an Object and let it become anything you like...
If drjava allows you to redeclare the variable within the same scope then its behavior is odd. Report the error.
This code should work:
Object s;
s="Foo";
System.out.println(s);
s=3;
System.out.println(s);
Is the only purpose of this.var to distinguish from outside variable names that might conflict?
Usually, this chances occurs when you are shadowing. Here's an example of shadowing.
public class YourClass
{
private int var;
}
It happens that you have this method:
public void yourMethod(int var)
{
this.var = var; // Shadowing
}
'this.var' happens to be your instance variable and is declared below your class. While on the other hand, in my example, var was a parameter.
Using this explicitly indicates the instance var as opposed to a constructor/method variable or parameter of the same name.
One use case is:
If your method/constructor parameter also named as var and you want to access instance variable in that method, then you may need to explicitly tell this.var to use instance variable.
Sometimes when you write a constructor the names of the variables that you are passing on as arguments, might have the same name as the instance variables you declared within your methods. So this.var refers to the actual instance variable.