Passing "this" in java constructor
Look into the following code:
public class ClassA {
private boolean ClassAattr = false;
public ClassA() {
ClassAHandler handler = new ClassAHandler(this);
}
}
public class ClassAHandler extends GeneralHandler {
ClassA ca = null;
public ClassAHandler(ClassA classa)开发者_JAVA百科 {
this.ca = classa;
}
}
I need to access ClassAattr
on some ClassAHandler
methods, among other attributes. Is there a way to do so without passing the origin class in the handler constructor. I don't really like how this solution "looks".
Passing this
to another method/object from inside the constructor can be rather dangerous. Many guarantees that objects usually fulfill are not necessarily true, when they are looked at from inside the constructor.
For example if your class has a final
(non-static
) field, then you can usually depend on it being set to a value and never changing.
When the object you look at is currently executing its constructor, then that guarantee no longer holds true.
As an alternative you could delay the construction of the ClassAHandler
object until it is first needed (for example by doing lazy initialization in the getter of that property).
Create a registerHandler(ClassA handler) method.
There is no way to create a handler for something the handler doesn't know about.
You could use inner classes, there is then a implicit parent-child relationship between the two instances. (But I don't know if it's really better).
public class ClassA {
private boolean ClassAattr = false;
public class ClassAHandler extends GeneralHandler {
public ClassAHandler() {
// can access ClassAattr
}
}
public ClassA() {
ClassAHandler handler = new ClassAHandler();
}
}
If you pass this
, the subclass will need to access the parent value with parent.classAattr
. We can wonder whether it's correct according to the law of demeter.
Another option then would be that ClassA
pass all the information that ClassAHandler
requires in the constructor. If the handler requires the value of ClassAttr
, pass it in the constructor.
public ClassA() {
ClassAHandler handler = new ClassAHandler( classAattr );
}
But the parameter is passed by value so I don't know if it works for you.
A third option would be to change the design a bit and have the boolean
be in the handler. Then ClassA
accesses the value of the child with handler.handlerAttr
. The child knows nothing about the parent, but the parent can access as much information in the child has he wants. This is better regarding the law of demeter.
public class ClassAHandler extends GeneralHandler {
boolean handlerAttr;
public ClassAHandler() {
}
}
public class ClassA {
private boolean ClassAattr = false;
public ClassA() {
ClassAHandler handler = new ClassAHandler(this);
classAttr = true;
}
}
public class ClassAHandler extends GeneralHandler {
ClassA ca = null;
public ClassAHandler(ClassA classa) {
this.ca = classa;
System.out.println(ca.classAttr);
}
}
So I have added the statement classAttr = true;
The System.out.println
statement will print false.
This is because the construction of ClassA
was not complete at that point.
So my suggestion is to add another method in classA
which will create the ClassAHandler
and then the classAHandler
will receive fully constructed ClassA
object
So that the code will look like.
public class ClassA {
private boolean ClassAattr = false;
public ClassA() {
classAttr = true;
}
public init() {
ClassAHandler handler = new ClassAHandler(this);
}
}
So that the code sequence will be new ClassA().init()
and will work perfectly
If I understand correctly, you need the handler to have a reference to ClassA
, but you don't want to set this up from within the constructor of ClassA
?
If that is the case, then you could separate construction from "wiring up" using a factory pattern, which will prevent your ClassA
from needing to know about the ClassAHandler
class. Kind of like this:
public class ClassA {
private boolean ClassAattr = false;
public ClassA() {
}
}
public class ClassAHandler {
private ClassA ca = null;
public ClassAHandler(ClassA classa) {
this.ca = classa;
}
}
public HandlerFactory {
public ClassAHandler createClassAHandler(ClassA classa) {
ClassAHandler handler = new ClassAHandler(classa);
return handler;
}
}
As others have said, passing this
is dangerous because the this
object has not finished running its constructor yet when it is received by the ClassAHandler
. Instead you should do something like this, which is akin to the factory pattern:
public class ClassA {
private boolean classAAttr = false;
public static ClassA newInstance() {
ClassA ca = new ClassA();
new ClassAHandler(ca);
return ca;
}
private ClassA() {}
}
public class ClassAHandler extends GeneralHandler {
ClassA ca = null;
public ClassAHandler(ClassA ca) {
this.ca = ca;
}
}
Of course now instead of writing new ClassA()
in the rest of your code you need to use ClassA.newInstance()
, but that's no real inconvenience.
I don't normally like the factory pattern, but I usually prefer this to the approaches in the other answers here because those require re-arranging your classes and inner classes in particular tend to complicate things. You also don't want to push extra responsibilities to the rest of your code and make it harder to set up new instances of ClassA
.
Write a getter method for ClassAattr, as
public boolean isClassAattr(){
return this.ClassAattr;
}
So that you can get access it as ca.isClassAattr();
You may make ClassAHandler an inner class of ClassA. It would have access to the members of ClassA.
there is nothing wrong with the code you pasted, however you can use a non static inner class to make things (arguably) cleaner:
public class ClassA {
private boolean ClassAattr = false;
public ClassA() {
ClassAHandler handler = new ClassAHandler();
}
class ClassAHandler extends GeneralHandler {
// magically sees the instantiating ClassA members and methods
}
}
精彩评论