📜 ⬆️ ⬇️

We return the child class from the parent. Optional

What is the purpose of this? For example, if you want to use " fluid interface ". However, if part of the methods are placed in the parent class, then they will not be able to make a full-fledged “fluid interface” - these methods are required to be brought (cast) to the child.

It would seem that sadness and only, but if you really want, then you can. Therefore, let's slightly break the rule and “notify” the parent class of its heirs.

So, there is an initial state ( commit ):

1) interface IChildParam , which knows about all the required methods
2) parent class BaseClass
1) ChildClass successor ChildClass
 public interface IChildParam { int getBaseParam(); int getChildParam(); <T> T setBaseParam(int i); <T> T setChildParam(int i); } 
 public abstract class BaseClass implements IChildParam { private int baseParam; public int getBaseParam() { return baseParam; } public BaseClass setBaseParam(int i) { this.baseParam = i; return this; } } 
 public class ChildClass extends BaseClass { private int childParam; public int getChildParam() { return childParam; } public ChildClass setChildParam(int i) { this.childParam = i; return this; } } 

')
And also, we will write a small test class in which we describe three possible options for calling the setBaseParam and setChildParam :
 public class ClassTest { public static final int BASE_PARAM = 1; public static final int CHILD_PARAM = 2; @DataProvider(name = "data") public static Object[][] data() { final ChildClass item0 = (ChildClass) new ChildClass().setBaseParam(BASE_PARAM); item0.setChildParam(CHILD_PARAM); final ChildClass item1 = (ChildClass) new ChildClass().setChildParam(CHILD_PARAM).setBaseParam(BASE_PARAM); final ChildClass item2 = ((ChildClass) new ChildClass().setBaseParam(BASE_PARAM)).setChildParam(CHILD_PARAM); return new Object[][]{{item0}, {item1}, {item2}}; } @Test(dataProvider = "data") public void testChildClass(final IChildParam item) throws Exception { Assert.assertEquals(item.getBaseParam(), BASE_PARAM); Assert.assertEquals(item.getChildParam(), CHILD_PARAM); } } 


And this is where the secret lies in the code - instead of calling the setChildParam method immediately after calling the setBaseParam method, you have to do a type conversion first and then setChildParam . And in any case, we will need an explicit type ChildClass to ChildClass

Well, let's correct this “misunderstanding”.

First, change the interface declaration - move the Generic type from the method level to the interface level:
 public interface IChildParam<T> { int getBaseParam(); int getChildParam(); T setBaseParam(int i); T setChildParam(int i); } 


Next, we will make changes to the declaration of the BaseClass class, adding to it the Generic-type of “himself”:
 public abstract class BaseClass<T extends BaseClass> implements IChildParam<T> { private int baseParam; public int getBaseParam() { return baseParam; } public T setBaseParam(int i) { this.baseParam = i; return (T) this; } } 


And the final step of the changes in the main code is to change the declaration in the child class:
 public class ChildClass extends BaseClass<ChildClass> { private int childParam; public int getChildParam() { return childParam; } public ChildClass setChildParam(int i) { this.childParam = i; return this; } } 


Now nothing prevents us from rewriting the code in the test with the full use of the “fluid interface”:
It was:
 final ChildClass item0 = (ChildClass) new ChildClass().setBaseParam(BASE_PARAM); item0.setChildParam(CHILD_PARAM); final ChildClass item1 = (ChildClass) new ChildClass().setChildParam(CHILD_PARAM).setBaseParam(BASE_PARAM); final ChildClass item2 = ((ChildClass) new ChildClass().setBaseParam(BASE_PARAM)).setChildParam(CHILD_PARAM); 
has become:
 final ChildClass item0 = new ChildClass().setBaseParam(BASE_PARAM); item0.setChildParam(CHILD_PARAM); final ChildClass item1 = new ChildClass().setChildParam(CHILD_PARAM).setBaseParam(BASE_PARAM); final ChildClass item2 = new ChildClass().setBaseParam(BASE_PARAM).setChildParam(CHILD_PARAM); 


Commit with result

PS: Of course, we didn’t break anything and the parent class didn’t know anything, remained in ignorance about his heirs, but ... But then he can now return from the method the same type as his heir, which was actually required .

UPD.0: Added an example for comment
UPD.1: Added an example for comment

Source: https://habr.com/ru/post/234945/


All Articles