Setting Outer Variable from Anonymous Inner Class

Setting outer variable from anonymous inner class

Java doesn't know that doWork is going to be synchronous and that the stack frame that result is in will still be there. You need to alter something that isn't in the stack.

I think this would work

 final Long[] result = new Long[1];

and then

 result[0] = st.getLong(4);

in execute(). At the end, you need to return result[0];

You might want to make a class because you don't like how it looks to use an array here, but this is the basic idea.

Modifying an outer variable within an anonymous inner class

The behavior you are referring to applies only to local variables or method/catch parameters. You can access and, potentially, modify instance members just fine

public class Example {

public void method() {
String localFoo = "local";
new Object() {
public void bar() {
foo = "bar"; // yup
System.out.println(localFoo); // sure
localFoo = "bar"; // nope
}
};
}

private String foo = "foo";
}

The anonymous Object inner class copies the value of localFoo for use within the println(..) invocation. However, for foo, it's actually "copying" the reference to the Example instance and referencing its foo field.

It's actually equivalent to

Example.this.foo = "bar";

How to change outer variable from anonymous inner class?

You're creating a Runnable class, but it actually never runs. You need to "start" it, by calling its start() method.

But you must also keep in mind, that when you start it inside the outerMethod(), it may not run before the Log method is called (since it will run in a separate thread) and the order in which the code is called is not guaranteed anymore.

How to use an outer variable in an anonymous inner class

In JLS 8.1.3. Inner Classes and Enclosing Instances:

When an inner class (whose declaration does not occur in a static
context) refers to an instance variable that is a member of a
lexically enclosing class, the variable of the corresponding lexically
enclosing instance is used.

Any local variable, formal parameter, or exception parameter used but
not declared in an inner class must be declared final.

It means you can only use an outside final variable or an enclosing class member in an anonymous inner class.

/*
* I suggest this solution, but not this approach,
* please be careful with multi-threading programming. :)
*/

// [...]
// private List<WMSMaterial> listMaterials; // or using a class member
// [...]
public void insertMaterial(final List<WMSMaterial> listMaterials) {

new Thread(){

public void run(){
com.ssn.acx.api.configuration.ParameterSet ps = com.ssn.acx.api.ACXObjectFactory.getConfigurationFactory().getLocalConfiguration().getParameterSet(com.ssn.acx.api.persistence.ACXPersistenceFactory.CFG_DEFAULT);
com.ssn.acx.api.persistence.ACXPersistenceFactory factory = com.ssn.acx.api.ACXObjectFactory.getPersistenceFactory(ps);
com.ssn.acx.api.persistence.ACXPersistenceSession session = factory.openSession();
com.ssn.acx.api.common.transaction.ACXTransaction tx = null;

try {
tx = session.beginTransaction("InsertMaterial");

for (WMSMaterial material : listMaterials) {
session.getPersistenceSession().insert(material);
}

tx.commit();

} finally { if (tx != null && !tx.closed()) { tx.rollback(); } session.close(); }//end of try-catch-finally block

}//end of run method

}.start(); //end of Thread Object creation

} //end of insertMaterial method

[Update]

As @AndyThomas points out in the comment, Java 8 has effectively final variable that you do not need to explicitly mark a variable as final:

Certain variables that are not declared final are instead considered effectively final:

  • It is not declared final.

  • It never occurs as the left hand side in an assignment expression
    . (Note that the local variable declarator containing the
    initializer is not an assignment expression.)

  • It never occurs as the operand of a prefix or postfix increment or
    decrement operator.

JLS 4.12.4. final Variables

Access method of outer anonymous class from inner anonymous class

Since Java 8 the solution is pretty easy. Just store the method reference in a variable.

ReturnsANumber v = new ReturnsANumber() {
int theNumber() {
return 119;
}

public int getIt() {

Supplier<Integer> supplier = this::theNumber;

ReturnsANumber w = new ReturnsANumber() {
int theNumber() {
return 1;
}

public int getIt() {
return supplier.get();
}
};

return w.getIt();
}
};

Storing outer object could also do the trick. But for inherited methods only:

interface ReturnsANumber {
int theNumber();
int getIt();
}

public int getIt() {
ReturnsANumber outer = this;

ReturnsANumber w = new ReturnsANumber() {
public int theNumber() {
return 1;
}

public int getIt() {
return outer.theNumber();
}
};

return w.getIt();
}

You can store the method reference or the outer object as a field also.

Update

@Holger proposed another workaround. You can pass your outer object to a lambda:

ReturnsANumber v = new ReturnsANumber() {
...
@Override
public int getIt() {
ReturnsANumber w = Optional.of(this).map(outer ->
new ReturnsANumber() {
int theNumber() {
return 1;
}
public int getIt() {
return outer.theNumber();
}
}).get();
return w.getIt();
}
};

Modify local variable from anonymous inner class

You can't modify the local variable. But if the local variable is an object reference, you can modify the object it refers to. For example:

public static void main(String[] args) {
final AtomicReference<String> value = new AtomicReference<String>("hello");

System.out.println(value); // prints "hello"

new Runnable() {
public void run() { value.set("goodbye"); }
}.run();

System.out.println(value); // prints "goodbye"
}

Best practice to access outer class variable in static nested class anonymous method Java

A static nested class InnerClass cannot access members of the enclosing instance, because an instance of InnerClass may not be owned by OuterClass. Imagine this:

public class Outer {
public String out = "out";

public static class StaticInner {
public void printEnclosing() {
System.out.println(Outer.this.out); // Let's say this compiles
}
}
}

public class Test {
public static void main(String[] args) {
StaticInner si = new StaticInner();
si.printEnclosing();
}
}

Note that an instance of Outer was never ever made. Then how is it possible to print the value of out?

What you need is inner class. Inner class cannot be instantiated by anyone other than the enclosing class, or its subclasses. This ensure that the only time you can instantiate the inner class is when there is already an instance of the outer class.

public class Outer {
public String out = "out";
public Inner inner = new Inner();

public class Inner {
public void printEnclosing() {
System.out.println(Outer.this.out); // "out" can be safely referenced
}
}
}

public class Test {
public static void main(String[] args) {
Inner inner = new Inner(); // Compile-time error

Outer out = new Outer();
out.inner.printEnclosing(); // This works, because you are calling method of the Inner class instance owned by Outer class.
}
}

Therefore, this is what your code should look like:

public class OuterClass extends CustomAppCompatActiivity {
private EditText city_et;
private final InnerClass inner = new InnerClass();
public class InnerClass extends DialogFragment {
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
okBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//the view to access
city_et.setText("");

getDialog().dismiss();
}
});
}
}

public InnerClass getInnerClass() {
return inner;
}
}

Update

For your concern on memory leak, I think the question is: What are the roles of InnerClass and OuterClass. Everytime you can create an InnerClass instance (or a collection of InnerClass instances) is when you have a new OuterClass. InnerClass needs the reference of OuterClass in order to call setText().

Therefore the contract here is: InnerClass can't exist without OuterClass, and an InnerClass always have an InnerClass. InnerClass will be garbage collected together with OuterClass.

If the relationship between InnerClass and OuterClass is that InnerClass can function without OuterClass, and all you need is the EditText city_et reference, then you should pass that reference in. Since InnerClass is independent from OuterClass now, you can use static nested class, or simply move it to another separate class.

public class OuterClass extends CustomAppCompatActiivity {
private EditText city_et;

public static class InnerClass extends DialogFragment {
private EditText city_et;

public InnerClass(EditText city_et) { // Pass a reference in
this.city_et = city_et;
}

@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
okBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//the view to access
city_et.setText("");

getDialog().dismiss();
}
});
}
}
}

This way, an OuterClass instance can be garbage collected whenever it is qualified to do so, independent of InnerClass. The only reference that cannot be garbage collected is that EditText instance.

Update 2

EJP pointed out that "Inner class cannot be instantiated by anyone other than the enclosing class, or its subclasses" is wrong, and after checking out, he's right. On top of that, the term "inner class" is the official term for "non-static nested class."

However, you should still use a static nested class or a separate class if you think that the DialogFragment subclass could live longer than the enclosing class.

Accessing outer class variable in inner class

Assuming your outer class is called Outer, from the scope of the inner class(non-static), Outer.this.foo to get at the field.

For example,

Outer.this.foo=new ArrayList<>();

where Outer is the name of the class and foo identifies the field.

You can also grab it directly as foo=new Baz() but it'll pick the inner field if there's a naming conflict due to shadowing.

if it's a static inner class, you need an explicit instance:

outerInstance.foo=new ArrayList<>();

or if the field to access is static, access it as usual with:

Outer.staticFoo=new ArrayList<>();


Related Topics



Leave a reply



Submit