I need to refactor class extracting abstract superclass.
E.g.
UpperClass {
NestedClass {
UpperClass.this.someMethod();
}
}
Like:
AbstractUpperClass {
NestedClass {
?????.this.someMethod();
}
}
After I plan inherit AbstractUpperClass in 2 classes UpperClass1 and UpperClass2.
But I don't know how to refactor this inner class because it inovokes method of enclosing class. Does it possible?
Thanks.
The trick here is knowing how the inner class works. It's essentially just a "normal", static class, but whose constructor implicitly gets a reference to the enclosing class. So, this:
public class TopLevel {
public void go() {
new Inner().bar();
}
public void foo() { }
public class Inner {
public void bar() {
TopLevel.this.foo();
}
}
}
is equivalent to this:
public class TopLevel {
public void go() {
new Inner(this).bar(); // explicitly passing in "this"
}
public void foo() { }
public static class Inner {
private final TopLevel parent; // note that we have this new field
public Inner(TopLevel parent) { // note this new constructor
this.parent = parent;
}
public void bar() { // we use the explicit reference instead
parent.foo(); // of the implicit TopLevel.this
}
}
}
So, with all that said, the way to refactor your inner class to be a top-level class is to add an explicit field referencing the UpperClass instance, and passing this reference into the NestedClass constructor. In other words, be like that second code snippet instead of the first.
Related
This question already has answers here:
Java superclass calls subclass method
(5 answers)
Closed 1 year ago.
Why does method() call the overridden subclass method2 instead of method2 in the BaseClass?
public class BaseClass {
public void method(){
System.out.println("method() called");
method2();
}
public void method2(){
System.out.println("method2() called");
}
}
public class ChildClass extends BaseClass {
public void method2(){
System.out.println("method2() from BaseClass");
}
}
public class Main {
public static void main(String[] args) {
ChildClass obj = new ChildClass();
obj.method();
}
}
This is the concept of Runtime polymorphism (Dynamic Method Dispatch). Because you are assigning the object (instance) of ChildClass to obj reference variable, it will call the method of child class.
Always the method of the class whose instance is created gets called first. If that method is not present in that particular child class, then the parent's inherited method gets called.
If you come from the C++ corner:
all instance methods (non-static) in Java are virtual.
All class methods (static) are NOT.
This is why your case happens.
This is also, why the Java compiler will complain (warn) that if you access a static method via an object, that you should call via the distinct class, because calls to the "static method of a object" could be ambiguous, because it could be two static methods with the same signature that get called.
Extending your Example:
package stackoverflow.staticcalls;
public class BaseClass {
public void method() {
System.out.println("method() called");
method2();
}
public void method2() {
System.out.println("method2() called");
}
static public void sayHello() {
System.out.println("BaseClass.sayHello()");
}
}
and
package stackoverflow.staticcalls;
public class ChildClass extends BaseClass {
public void method2() { // compiler warning: The method method2() of type ChildClass should be tagged with #Override since it actually overrides a superclass method
System.out.println("method2() from BaseClass");
}
public void originalCallToBaseMethod2() {
super.method2(); // will run BaseClass.method2()
}
static public void sayHello() {
System.out.println("ChildClass.sayHello()");
}
}
and
package stackoverflow.staticcalls;
public class Main {
public static void main(final String[] args) {
final ChildClass obj = new ChildClass();
System.out.println("\nCalling obj.method(); ...");
obj.method();
System.out.println("\nCalling obj.sayHello(); ...");
obj.sayHello(); // compiler warning: The static method sayHello() from the type ChildClass should be accessed in a static way
System.out.println("\nCalling ChildClass.sayHello(); ...");
ChildClass.sayHello(); // the proper call
System.out.println("\nCalling BaseClass.sayHello(); ...");
BaseClass.sayHello(); // but you can also explpicitly call the other method
System.out.println("\nCalling obj.originalCallToBaseMethod2(); ...");
obj.originalCallToBaseMethod2(); //
}
}
Here you see the examples to what I said.
Note: In the last call in Main.main() we still can call BaseClass.method2(), but not directly. We have to be within ChildClass to do that, and it's done via the super keyword/reference.
A little off-topic note, to complete addressing patterns:
If you're inside an inner class and need to call to a overshadowed name in the outer class, you can use Outer.this.method():
package stackoverflow.staticcalls;
import stackoverflow.staticcalls.OuterInner.Outer.Inner;
public class OuterInner {
class Outer {
void method() {
System.out.println("OuterInner.Outer.method()");
}
class Inner {
void method() {
System.out.println("OuterInner.Outer.Inner.method()");
}
void callOuter() {
Outer.this.method();
}
}
}
public static void main(final String[] args) {
final OuterInner oi = new OuterInner();
final Outer outer = oi.new Outer();
final Inner inner = outer.new Inner();
System.out.println("\nCalling inner.method(); ...");
inner.method();
System.out.println("\nCalling inner.callOuter(); ...");
inner.callOuter();
}
}
I have an identical method that is repeated in every subclass and I'd like to refactor it to just one method in a superclass.
public class SubClass1 extends SuperClass {
private BoltHexHead bolt;
private void computeFoo() {
//Foo formula is identical in all subclasses. Need to move up
setFoo(bolt.getDiameter() + bolt.getPitch() + bolt.getTpi());
}
private void computeBar() {
//computeBar method in all subclasses but Bar formula is different amongst all subclasses
setBar(bolt.getDiameter() - 2*bolt.getPitch() - 3*bolt.getTpi());
}
private void computeSeparation() {
//computeSeparation method only exists for a Subclass 1
setSeparation(bolt.getLength() - 2*nut.getFlatDia());
}
public class SubClass2 extends SuperClass {
private BoltFlatHead bolt;
private void computeFoo() {
//Foo formula is identical in all subclasses. Need to move up
setFoo(bolt.getDiameter() + bolt.getPitch() + bolt.getTpi());
}
private void computeBar() {
//computeBar method here is different than in Subclass1
setBar(bolt.getDiameter() - 4*bolt.getPitch() - 1/3*bolt.getTpi());
}
private void computeProtrusion() {
//computeProtrusionmethod only exists for a Subclass 2
setProtrusionmethod(bolt.getThreadAngle() - 6*bolt.getTpi());
}
Initially I posted that bolt wasn't getting set in the SuperClass but was in the SubClass. What I got working after my initial post was the following
public abstract class SuperClass {
protected Bolt<?> bolt; <-- this was added but uses wildcard
...bolt getters/setter
protected void computeFoo() {
//Foo formula pulled up from all subclasses
setFoo(bolt.getDiameter() + bolt.getPitch() + bolt.getTpi());
}
}
public class SubClass1 extends SuperClass {
//private BoltHexHead bolt; <-- commented this out in each subclass
}
This is a JSF app and in each controller bean I instantiate the specific joint attribute subclass and then set the specific bolt. It was an earlier design decision to use setters for setting the bolt (and other properties) in the subclass rather than doing it with the Constructor; but one refactor at a time.
Controller for a Bolt Analysis using a HexHead Bolt
private SubClass1 sc1 = new SubClass1();
private BoltHexHead bolt;
sc1.setBolt(bolt);
sc1.computeResults();
Controller for a Bolt Analysis using a FlatHead Bolt
private SubClass2 sc2 = new SubClass2();
private BoltFlatHead bolt;
sc2.setBolt(bolt);
sc1.computeResults();
So my question is, is it OK to use wildcard Bolt<?> bolt or is there a better approach?
I'm just trying to put a identical/duplicate method from all my subclasses into the parent but one of the variables (bolt) isn't getting set
Thats because in java you cannot override fields.
So your variables "private B bolt;" in your superclass and "private BoltHexHead bolt;" in your subclass are two different things. They actually both exist at the same time.
What you are trying to do actually isn't that complicated. You just need to clean up your code:
Only define "private B bolt;" and its setters/getters once in your superclass.
Only use those getters/setters to access bolt
If you want your subclass to have a "bolt" of the type "BoltHexHead" then define the generic parameter as such in the class definition ("extends JointAttribute<BoltHexHead>" instead of "extends JointAttribute<Bolt<BoltSpec>>")
A simple example for demonstration purspose:
Superclass:
public class Superclass<T> {
private T value;
protected T getValue() {
return value;
}
protected void setValue(T value) {
this.value = value;
}
protected void print() {
if(getValue()==null) {
System.out.println("NULL");
} else {
System.out.println(getValue().toString());
}
}
}
Subclass1:
public class Subclass extends Superclass<String> {
public Subclass() {
}
public static void main(String[] args) {
Subclass subclass= new Subclass();
subclass.print();
subclass.setValue("test");
subclass.print();
}
}
Subclass2:
public class Subclass2 extends Superclass<Integer> {
public Subclass2() {
}
public static void main(String[] args) {
Subclass2 subclass= new Subclass2();
subclass.print();
subclass.setValue(3);
subclass.print();
}
}
When I tried to understand how to work with collections in java, I realised that I don't understand how polymorphism works for inner classes.
Simple code example:
class Parent {
public static void main(String[] args) {
new Parent().newInnerClass().myMethod();
new Child().newInnerClass().myMethod();
}
public I newInnerClass() {
return new InnerClass();
}
private final class InnerClass implements I {
#Override
public void myMethod() {
System.out.println("parent inner class");
foo();
}
}
public void foo() {
System.out.println("foo from parent");
}
}
class Child extends Parent {
public void foo() {
System.out.println("foo from child");
}
}
interface I {
void myMethod();
}
result:
parent inner class
foo from parent
parent inner class
foo from child
Therefore first link affects the third method invocation. It is surprising to me.
Initially I thought that needed methods selected accordind to the link. But new Parent().newInnerClass() and new Child().newInnerClass() are links to InnerClass from Parent.
Can you clarify my misunderstanding?
P.S.
If InnerClass was in Child and extended InnerClass from Parent - this behaviour wouldn't be surprising for me.
There are no special rules for polymorphism in inner classes.
Inner class differs from regular class in two things:
Inner class holds an implicit reference to its containing object
Inner class can access private methods of its containing class (not relevant here)
That's how you can rewrite your example without inner class:
class Parent {
...
public I newInnerClass() {
return new NotInnerClass(this);
}
...
}
class NotInnerClass implements I {
private final Parent containingObject;
public NotInnerClass(Parent containingObject) {
this.containingObject = containingObject;
}
#Override
public void myMethod() {
System.out.println("parent inner class");
containingObject.foo();
}
}
This code produces the same output as your, because when you invoke
new Child().newInnerClass().myMethod();
containingObject is a Child and containingObject.foo() is a regular polymorphic call.
When you use inner class, compiler does the same thing behind the scenes.
I have tree classes.
class MyObject{
public void DoSomething()
{
here I need to call method add from class base.
}
}
class base
{
protected final void add(){}
}
class extended extends base {
private MyObject pObject = new MyObject();
...
{
pObject.DoSomething();
}
}
I could have created class for each variation that extends class extended, but the type what I need to use becomes available only after class extended is already initiated.
How do I call base.add() from MyObject inner method?
You can do it in a couple of ways:
Have a reference of your extended class in MyObject class. When you instantiate MyObject variable in extended class, pass it the reference of extended.
Something like this:
class MyObject{
private base baseObj;
public MyObject(base baseObj){
this.baseObj = baseObj;
}
public void DoSomething()
{
//here I need to call method add from class base.
//use baseObj to call the methods
}
}
class base
{
protected final void add(){}
}
class extended extends base {
private MyObject pObject;
...
public extended(){
pObject = new MyObject(this);
}
{
pObject.DoSomething();
}
}
Declare the methods in base class static. This way you can call the methods without requiring an instance of the base class.
Something like this:
class MyObject{
public void DoSomething()
{
//here I need to call method add from class base.
//call like this
base.add();
}
}
class base
{
protected static final void add(){}
}
class extended extends base {
private MyObject pObject;
...
public extended(){
pObject = new MyObject(this);
}
{
pObject.DoSomething();
}
}
One more thing: This is off-topic, but you might want to read about Java Naming Conventions. Having class names start with lowercase is something that you wouldn't find in the naming conventions.
dummy code like this:
class MyObject{
public void DoSomething(Base base)
{
base.add();
}
}
class extended extends base {
private MyObject pObject = new MyObject();
...
{
pObject.DoSomething(this);
}
}
I'm not sure if my question title describes my situation aptly, so my apologies if it doesn't! Anyway, let's say I have the following code snippet (visibility is as stated):
public class ChildClass extends ParentClass {
// more code
private void myMethod() {
MyClass mine = new MyClass() {
public void anotherMethod() {
// insert code to access a method in ParentClass
}
};
}
}
Is it possible for code within anotherMethod() to access a protected method found in ParentClass? If so, how can this be done?
I've tried something like...
(ParentClass.this).parentMethod();
...but obviously it doesn't work due to scope issues.
This compiles fine:
class MyClass {
}
class ParentClass {
protected void parentMethod() {
}
}
class ChildClass extends ParentClass {
private void myMethod() {
MyClass mine = new MyClass() {
public void anotherMethod() {
parentMethod(); // this works
}
};
}
}
A non-static inner class can access all methods of the enclosing class as if it were it's own methods:
public class Test {
public int getOne() {
return 1;
}
public class Inner {
public int getEnclosingOne() {
return getOne(); // this works...
}
}
}
A static inner class can not, as a static inner class is not bound to an instance of the parent class. That can only call static methods on the enclosing class.
As for methods when taking into account inheritance, an method in a non-static inner class can use all the methods of the enclosing (outer) class.
The interesting part is Test2.super.getOne() which indeed obtains getOne() from Test2.super, which is a Test. This is just like Test2 would access the method, namely using super though prefixed with Test2 to indicate you're accessing the namespace of the outer class.
public class Test2 extends Test {
public int getInnerOuterParentOne() {
Inner2 inner2 = new Inner2();
return inner2.getOuterParentOne();
}
public int getInnerOuterOne() {
Inner2 inner2 = new Inner2();
return inner2.getOuterOne();
}
public int getOne() {
return 2;
}
public class Inner2 {
public int getOuterOne() {
return getOne();
}
public int getOuterParentOne() {
return Test2.super.getOne();
}
}
public static void main(String[] args) {
Test2 test2 = new Test2();
System.out.println(test2.getInnerOuterOne()); // 2
System.out.println(test2.getInnerOuterParentOne()); // 1
}
}
There is no way to access "parent class method" in Java, irrelatively to visibility (except for super.parentMethod() in subclass's parentMethod()).
That is, if ChildClass overrides parentMethod(), there is no way to call ParentClass.parentMethod() (bypassing ChildClass.parentMethod()) from other methods of ChildClass.
However, if ChildClass doesn't override parentMethod(), that method is inherited by ChildClass, so that you can access it as a ChildClass's method, i.e. simply as parentMethod().