常规链式调用
对于链式调用,只需要在某些操作方法中返回this即可:
class A {
protected String name;
public A setName(String name) {
this.name = name;
return this;
}
public String getName() {
return name;
}
}如上,使用时:
String name = new A().setName("nameA").getName();继承
然而,父类定义的链式方法,并不能被子类有效继承:
class B extends A {
protected Integer age;
public B setAge(Integer age) {
this.age = age;
return this;
}
public Integer getAge() {
return age;
}
}使用时:
// 编译器报错。
String name = new B().setName("nameB").setAge(18).getName();此时编译器会报错。这是因为调用setName()时,尽管此时实际的this是B类型,但返回的类型是A,而A并没有setAge()方法。
因此,对其进行调整:
// 可以运行。
String name = new B().setAge(18).setName("nameA").getName();这样就能顺利运行。因为调用setAge()返回的类型是B,而B继承了A的方法,因此可以继续调用setName()。setName()之后调用的getName()也是A的方法,因此虽然setName()返回的类型是A,却并不会影响到getName()的调用。
但这样就存在一个问题:在链式调用中,必须先调用子类的方法,然后再调用父类的方法。这种限制是非常大的,对使用者非常不友好。
重载解决
一种最简单的解决方案就是重载:
class B extends A {
protected Integer age;
public B setAge(Integer age) {
this.age = age;
return this;
}
public Integer getAge() {
return age;
}
@Override
public B setName(String name) {
this.name = name;
return this;
}
}在子类B中,对父类需要返回this的方法进行重载,令其返回B类型。这样即可解决前面的问题:
// 可以运行。
String name = new B().setName("nameB").setAge(18).getName();但该方案比较繁琐,有多少返回this的方法就得重载多少次。并且重载只对本类有效,若本类还有子类,那么只能继续重载。
因此,适用于重载来解决链式调用继承问题的场景为:
父类返回this的方法很少。
只有一级子类,不存在孙子类或者更深层级的子类。
泛型解决
既然问题是父类返回this时的类型导致的,那么令其直接返回子类的类型不就可以解决了么。
基于此思路,可以使用泛型方式:
class A<T> {
protected String name;
public T setName(String name) {
this.name = name;
return (T)this;
}
public String getName() {
return name;
}
}
class B extends A<B> {
protected Integer age;
public B setAge(Integer age) {
this.age = age;
return this;
}
public Integer getAge() {
return age;
}
}为父类A添加泛型,在子类B定义时将自身作为泛型参数传入。
// 可以运行。
String name = new B().setName("nameB").setAge(18).getName();该方法不需要额外多写重载代码,更加简洁。
但此时若直接使用A类,依然要传入一个泛型参数:
// 编译器报错。
String name = new A().setName("nameA").getName();
// 可以运行。
String name = new A<A>().setName("nameA").getName();这是因为若不传入泛型参数,则会默认取Object类型。故而此时setName()返回的类型是Object。由于Object并没有getName()方法,因此会报错。
基于此,考虑为A传入一个派生于A的泛型,这样即使取默认值,也是A的派生类:
class A<T extends A> {
...
}
// 可以运行。
String name = new A().setName("nameA").getName();这样即可较好地解决上述所有问题。
对于只存在一级派生关系的类,可直接使用该方案。
多级派生
上述方案只适用于一级派生类。若派生层级有多级,依然是存在问题的:对B的子类C,setName()返回的类型是B,而非C。于是又遇到了前面的问题。于是再次进行改进:
class A<T extends A> {
...
}
class B<E extends B> extends A<E> {
...
}
class C<K extends C> extends B<K> {
...
public K setNumber(Integer number) {
this.number = number;
return (K)this;
}
}这样即可解决多级派生的链式调用继承问题。但要注意的是子类必须给出泛型参数:
// 以下代码编译器报错。
String nameB = new B().setName("nameB").setAge(18).getName();
String nameC = new C().setName("nameC").setNumber(99).setAge(18).getName();
// 以下代码均可顺利运行。
String nameA = new A().setName("nameA").getName();
String nameB = new B<B>().setName("nameB").setAge(18).getName();
String nameC = new C<C>().setName("nameC").setNumber(99).setAge(18).getName();总结
链式调用是为了简洁与易懂。但java本身对链式调用的支持并不是很友好。考虑到实际情况,建议使用链式调用仅局限在父子层级中,不要涉及更深的层级。

0条评论
点击登录参与评论