Use a class field in a lambda [duplicate] - java

This question already has answers here:
Java lambdas have different variable requirements than anonymous inner classes
(3 answers)
Unexpected error using lambdas in Java 8
(1 answer)
Closed 5 years ago.
I don't understand this behavior.
This piece of code complies:
public class A {
private String s;
private Function<String, String> f = e -> s;
public A(String s) {
this.s = s;
}
}
But if I make s final, then I get a compiler error:
public class A {
private final String s;
private Function<String, String> f = e -> s; // Variable 's' might not have been initialized
public A(String s) {
this.s = s;
}
}
Why is that? If it were the other way around, I'd understand, but how is that the compiler complains when I declare a field final (which forces me to initialize its value in the constructor), and it's OK for it when it's not final?

It has nothing to do with the lambda, this example has the same error:
public class Test {
private final String a;
private String b = a; // // Variable 'a' might not have been initialized
public Test(String a) {
this.a = a;
}
}
It's because the initialization at the place of declaration is executed before the constructor. Therefore, at the place of the declaration of b, a is still not initialized.
It's clear when you use this example:
public class Test {
private String a = "init";
private String b = a;
public Test(String a) {
this.a = a;
}
public static void main(String[] args) {
System.out.println(new Test("constructor").b);
}
}
When you run it, it prints "init" (the value to which field a was originally assigned) and not "constructor" because the initialization of b took place before running the constructor.
The lambda from your example makes the it a bit more convoluted, because we could expect that since the access to a is deferred, it would be OK with the compiler, but apparently the compiler just follows the general rule of "don't access the variable before it's initialized".
You can bypass it using an accessor method:
public class Test {
private final String a;
private String b = getA(); // allowed now, but not very useful
private Function<String, String> f = e -> getA(); // allowed now and evaluated at the time of execution of the function
public Test(String a) {
this.a = a;
}
public static void main(String[] args) {
System.out.println(new Test("constructor").b); // prints "null"
System.out.println(new Test("constructor").f.apply("")); // prints "constructor"
}
public String getA() {
return a;
}
}

A non final member variable will always be initialized (since it has a default value - null in the case of your String variable), so there's no chance of it being uninitialized.
On the other hand, a final variable may only be initialized once, so I'm assuming it is not initialized to its default value.
The closest related thing I found is in JLS 4.12.4.:
4.12.4. final Variables
A variable can be declared final. A final variable may only be assigned to once. It is a compile-time error if a final variable is assigned to unless it is definitely unassigned immediately prior to the assignment
I assume we can understand this last sentence to mean that a final variable is not assigned a default value, since otherwise you'll get a compile time error at this.s = s;.
A better JLS reference (thanks to Holger) is JLS 16:
Chapter 16. Definite Assignment
For every access of a local variable or blank final field x, x must be definitely assigned before the access, or a compile-time error occurs.
The rational behind this requirement is that (in your example) the lambda expression could be invoked before s is initialized:
public A(String s) {
String v = f.apply("x"); // this.s is not initialized at this point
// so it can't be accessed
this.s = s;
}
Note that you can initialize the lambda expression in the constructor after initializing the final variable (I changed the name of the argument to be different than the member variable, so that the lambda expression won't grab that local variable):
public A(String so) {
// f = e -> s; // Error: The blank final field s may not have been initialized
this.s = so;
f = e -> s; // works fine
}

This also possible way to do
public class A {
private final String s;
private Function<String, String> f;
public A(String s) {
this.s = s;
this.f = e -> s;
}
}

Related

What is the thinking behind local variable is final but outer class field is not when using inner class

inner class uses outer local variable is through its constructor like this InnerClass(String outerVar)
inner class uses outer field is through this.
I read it from an article that programmers who use Java are not used to to having variable changed both sides, otherwise it causes a AAD (Action At remote Distance).
If that is true, outer field can be changed both sides causes the same AAD problem.
What are the thinkings behind local var is final while field is not.
void test() {
var a = "a"; // outer local variable
class InnerClass {
void print() {sout(a);} // InnerClass(String a)
}
}
String a = "a" // outer field
void test() {
class InnerClass {
void print() { a = newA;} // OuterClass.this.a = newA
}
sout(a) // newA
}
Local variables are allocated on the stack and invalidated as soon as the method ends while fields are allocated on the heap.
If the inner class tried to access a local variable after the method ended, it would access invalid memory (possibly overwritten by something else). As fields are allocated on the heap, this is no problem because those are not invalidated until there is no reference to the object.
If the variable is final, the inner class can just access a copy as the copy will always be the same as the original variable (cannot be changed).
Using a copy with non-(effectively) final variables would cause logical errors if you would modify the variable later in the method as the copy wouldn't catch up (or the original value wouldn't ve updated if the copy us changed).
Objects are allocated on the heap, meaning those are shared between methods and threads. Objects and their instance variables exist as long as there is a reference to them.
However, not that caution should be taken when working with multiple threads as you may encounter synchronisation and visibility issues (not the topic of this question, though).
An inner class has a hidden unnamed field referencing the outer class. For the sake of this answer, we'll name it $outer.
The first version:
public class OuterClass {
void test() {
var a = "a"; // Local variable
class InnerClass {
void print() { sout(a); } // sout("a")
}
new InnerClass().print();
}
}
That actually gets compiled to:
public class OuterClass {
void test() {
final String a = "a";
new OuterClass$0$InnerClass(this, a).print();
}
}
final class OuterClass$0$InnerClass {
private final OuterClass $outer;
private final String a;
OuterClass$0$InnerClass(OuterClass $outer, String a) {
this.$outer = $outer;
this.a = a;
}
void print() { sout(this.a); } // sout("a")
}
As you can see, the local variable gets copied. The enforcement of final is to prevent the confusion that would occur if the local variable were changed after the value was copied.
The second version:
public class OuterClass {
String a = "a"; // Outer field
void test() {
class InnerClass {
void setA(String newA) { a = newA; }
}
new InnerClass().setA("b");
sout(a); // sout("b")
}
}
Which gets compiled to:
public class OuterClass {
String a = "a"; // Outer field
void test() {
new OuterClass$0$InnerClass(this).setA("b");
sout(a); // sout("b")
}
}
class OuterClass$0$InnerClass {
private final OuterClass $outer;
OuterClass$0$InnerClass(OuterClass $outer) {
this.$outer = $outer;
}
void setA(String newA) { this.$outer.a = newA; }
}
Since the value of field a is not copied, there is no need to enforce final behavior.

Inheritance rules in java

I have a question about the basics of java. I have the s attribute in each class. The value of s gotten by the instance of the class is different when I use the accessor (getS()). Is there a rule for this case?
The output of the main is :
x.s = One
x.getS() = Three
The classes definition :
package com;
import com.Test1.A;
import com.Test1.B;
public class Test1
{
public static class A
{
public static String s = "One";
public int x = 10;
public String getS()
{
return this.s;
}
}
public static class B extends A
{
public final String s = "Two";
public String getS()
{
return this.s;
}
}
public static class C extends B
{
public static int x = 1;
public static String s = "Three";
public String getS()
{
return this.s;
}
}
public static void main(String [] args)
{
A x = new C();
System.out.println("x.s = "+x.s);
System.out.println("x.getS() = "+x.getS());
}
}
The access of the field (x.s) is resolved through the compile-time type of x (which is A, so A's x ["One"] is returned).
The access through the getter (x.getS()) is resolved through the runtime type of x (which is C, so C's x ["Three"] is returned).
Some other examples:
((B) x).s will return "Two"
((C) x).s will return "Three"
((A) x).getS() will return "Three"
((B) x).getS() will return "Three"
(I leave the why as an exercise for the reader)
As an aside: the result does not change when
static is removed from String s = "One" in A, or
method public String getS() is removed from classes B and C, or
both of the above
Please read #Mike Nakis' answer as well.
One final remark on the code: the import-statements can be removed.
The static keyword in front of your variables probably isn't what you intended and it pushes you into a weird corner of Java's semantics.
Here's what you probably intended:
public class Test1 {
public static class A {
public String s = "One";
public int x = 10;
public String getS() {
return this.s;
}
}
public static class B extends A {
public B() {
this.s = "Two";
}
public String getS() {
return this.s;
}
}
public static class C extends B {
public C() {
this.s = "Three";
}
public String getS() {
return this.s;
}
}
public static void main(String[] args) {
A x = new C();
System.out.println("x.s = " + x.s);
System.out.println("x.getS() = " + x.getS());
}
}
This prints
x.s = Three
x.getS() = Three
as you'd expect.
The main difference between what you wrote and what I wrote is that without the static, we're declaring that every A has an s field; with static, we're saying that the concept of A-ness has an associated s idea. This is useful in some limited circumstances (for instance, the Java Integer class has a static field called MAX_VALUE because the maximum possible integer is associated with the concept of integers; every integer doesn't have its own individual max value), but it's probably not the first concept you want to learn.
There are, of course, rules for everything.
The expression x.s is problematic, because you are accessing a static field via an instance reference, and the compiler / IDE should have been issuing a warning for it. So, what this is telling me is that you are probably trying to compile java without important warnings enabled. Don't do that, if this way you go, only pain you will find. Read up on how to enable warnings on your compiler / IDE and enable as many of them as you possibly can.
The fix for the warning would be to replace x.s with A.s, and that would make clear exactly what is happening.
The s attribute that you are speaking of (more commonly referred to as 'field' in the java world) has been declared as static in some cases, but non-static in one case, and this does not look intentional. Generally, static is to be used only in very special cases, don't use it if you do not have a very good reason for doing so.
Also, some fields are final, some aren't, and this does not look intentional, either. You need to be very careful with these things.
Other than that, Turing85's answer basically covers it.

Referring to instance variable with same name as a local variable in a constructor?

This is a mostly theoretical example, but how do I refer to the instance variable "a" in the constructor, but not the current object variable "this.a", after a local variable "a" has been declared?
public class Foobar {
public int a = 0;
public Foobar(int a) {
this.a = a;
}
public Foobar(int b, int c) {
a = b * c;
int a = c; //after this point, is there any way of refer to the instance variable a but not a in the current object (i.e. this.a)?
}
public int getA() {
return this.a;
}
public static void main(String[] args) {
Foobar foo = new Foobar(3);
Foobar bar = new Foobar(3, 1);
System.out.println(foo.getA());
System.out.println(bar.getA());
}
}
There is no class variable in your code.
If you want a class variable, use static int a. Of course, you won't be able to refer to a class variable using this.a: that only works with instance variables.
And, of course, you can't have a class variable and an instance variable with the same name.
If you override the class variable with a local variable, (bad idea - don't do that - most IDEs will warn you) then can refer to the class variable with Foobar.a

Changing the value being passed through the parameter

I was testing whether you can change the value of the static variable x by passing it through a parameter, but I found out that you can't do it like that.
public class Test {
static int x;
static void changeX(int x_) {
x_ = 50;
}
public static void main(String args[]) {
changeX(x);
System.out.println(x);//it prints out zero because the static variable did not get changed
}
}
If we do it like this, we can change it:
public class Test {
static int x;
static void changeX(int x_) {
x = 50;
}
public static void main(String args[]) {
changeX(x);
System.out.println(x);
}
}
Which means you have to directly reference to the static variable in order to change it. Okay. Now. My question is, is there a way to change a class variable by just passing it through the parameter, without referencing it in the implementation? Basically, is there a way to use the first way somehow? Thanks.
You can achieve what you are asking, but you need to be aware of the subtle limitations. You can modify an object reference passed into a method, but you cannot reassign the reference in the method and have the original object be changed.
Java is always pass by value. Period.
Object references, are passed by value, but it is through those references (which point to the same memory location), that you can modify objects inside of methods, through the parameter.
You cannot modify primitives (int, float, boolean, etc) in this manner, they are always passed by value. You also cannot modify immutable objects (such as String), as they cannot be changed using a public interface.
Consider this simple example, which tests creating an object with some modifiable fields, by-value, and by-reference (it's modifying by object reference, that itself is passed by value):
public class ParameterPassingTest {
static SomeObject someStaticObject;
public static void main(String[] args) {
// initialize a static object reference
someStaticObject = new SomeObject("I am a static Object", 10);
System.out.println("My static object before: " + someStaticObject);
// try modifying the reference by value
modifySomeObjectByValue(someStaticObject);
// try printing the value, it will be the same
System.out.println("My static object after mod-by-value: " + someStaticObject);
// now, try modifying by object reference
modifySomeObjectByReference(someStaticObject);
// print again. new values should be observed
System.out.println("My static object after mod-by-reference: " + someStaticObject);
}
// this method tries to modify the original object by assigning directly to the method parameter. It won't work.
public static void modifySomeObjectByValue(SomeObject someObject) {
SomeObject newObject = new SomeObject("I am another object, from a local method", 20);
someObject = newObject; // try to modify the original object by assigning to the parameter directly
}
// this method tries to modify the original object by using the object's public interface. It works.
public static void modifySomeObjectByReference(SomeObject someObject) {
// try to modify the original object by using the reference passed in
someObject.setaString("I have been modified by a method");
someObject.setAnInt(50);
}
}
// simple, generic class object with some fields.
class SomeObject {
String aString;
int anInt;
public SomeObject(String aString, int anInt) {
this.aString = aString;
this.anInt = anInt;
}
public String getaString() {
return aString;
}
public void setaString(String aString) {
this.aString = aString;
}
public int getAnInt() {
return anInt;
}
public void setAnInt(int anInt) {
this.anInt = anInt;
}
#Override
public String toString() {
return "aString = " + getaString() + " | anInt = " + getAnInt();
}
}
This produces output:
My static object before: aString = I am a static Object | anInt = 10
My static object after mod-by-value: aString = I am a static Object | anInt = 10
My static object after mod-by-reference: aString = I have been modified by a method | anInt = 50
Java passes primitives always as value. Objects on the other hand are always passed as reference. In your second example, you access the static attribute x and not the parameter x_.
Furthermore, static does not protect an attribute from being rewritten. It binds an attribute to the class (without static attributes are bound to objects). Maybe you meant final?
EDIT: corrected a typo.

members initializing using "this"

here is my problem
class A{
private B b = new B(this); // line 2
A(){}
}
This is just an ex. code and works fine. But i have a doubt about this is used to current reference (instance of A). Class initializing happens before to get a class instance. So how can we put this in line 2. i asked does instantiation happen before initializing?
You bring up an interesting point. Here is a contrived instructional example that demonstrates a run time problem that can happen when using your example.
class A {
private boolean isInitialized = false;
private final B b = new B(this);
public A() {
initialize();
}
private void initialize() {
isInitialized = true;
}
public boolean isInitialize() {
return isInitialized;
}
public B getB() {
return b;
}
}
class B {
private boolean isInitialized = false;
final private A a;
public B(final A a) {
this.a = a;
initialize();
System.out.println("inB: a.isInitialize()=" + a.isInitialize());
}
private void initialize() {
isInitialized = true;
}
public boolean isInitialize() {
return isInitialized;
}
}
public static void main(final String[] args) {
final A a = new A();
System.out.println("inMain: a.isInitialize()=" + a.isInitialize());
System.out.println("inMain:a.getB().isInitialize()=" + a.getB().isInitialize());
}
Output:
inB: a.isInitialize()=false
inMain: a.isInitialize()=true
inMain:a.getB().isInitialize()=true
Using the passed reference to class A within class B runs the real risk of using an object that is not fully initialized.
Be careful.
This is not class initialization (try to debug new ClassA() step by step), it is actually instance initialization.
There can be some problems if the constructor (from ClassB) calls some functions from ClassA, which access some fields in ClassA that are not initialized.
Edit: this is done before the constructor is called.
this is used correctly. The constructor doesn't need to be called at all.
No need for changes, everything is fine. this is a valid reference to A.
this will show its existence when you create an object of class A. Instance variable are assigned after object creation and static variable are initialize as soon as class loads and also before creations of any object.
you cannot use above initialization in static block
static {
private B b = new B(this); // compiler error. you cannot use 'this' in static context.
}

Categories