组合和继承
这是两种不同的实现方式,不能混用
java
public class GetterErrorDemo {
private static final String TAG = GetterErrorDemo.class.getSimpleName();
public static class Node {
private final String name;
public Node(String name) {
this.name = name;
}
@NonNull
@Override
public String toString() {
return "Node{" + name + '}';
}
}
public static class BaseInfo {
private Node mNode;
public Node getNode() {
return mNode;
}
public void setNode(Node node) {
this.mNode = node;
}
@NonNull
@Override
public String toString() {
// 关键点:打印的是字段 mNode,而不是 getNode()
return "BaseViewInfo{node=" + mNode + '}';
}
}
public static class ChildInfo extends BaseInfo {
private final BaseInfo origin;
private String mLabel; // child 自己的属性
public ChildInfo(BaseInfo origin) {
this.origin = origin;
}
@Override
public Node getNode() {
return origin.getNode(); // getter 重写,委托给原始控件
}
public void setLabel(String label) {
this.mLabel = label;
}
@NonNull
@Override
public String toString() {
// 父类字段 mNode 从没被赋值 → 打印出 null,但 getNode() 却非空
return "ChildInfo{" + super.toString() + ", label=" + mLabel + '}';
}
}
public static void demo() {
BaseInfo origin = new BaseInfo();
origin.setNode(new Node("node1"));
ChildInfo child = new ChildInfo(origin);
child.setLabel("我是child自己的属性");
ArrayList<BaseInfo> list = new ArrayList<>();
list.add(origin);
list.add(child);
for (BaseInfo baseInfo : list) {
Log.d(TAG, "info = " + baseInfo);
}
origin.setNode(new Node("node2"));
Log.d(TAG, "--- after origin.setNode(node2) ---");
for (BaseInfo baseInfo : list) {
Log.d(TAG, "info = " + baseInfo);
}
Log.d(TAG, "origin.getNode().equals(child.getNode()) = " + origin.getNode().equals(child.getNode()));
}
}
对于如上代码,ChildInfo即继承了BaseInfo,又组合了BaseInfo,导致toString打印的mNode始终为空,而重写的getNode()又不为空,导致增加排查难度
info = BaseInfo{node=Node{node1}}
info = ChildInfo{BaseInfo{node=null}, label=我是child自己的属性}
--- after origin.setNode(node2) ---
info = BaseInfo{node=Node{node2}}
info = ChildInfo{BaseInfo{node=null}, label=我是child自己的属性}
origin.getNode().equals(child.getNode()) = true
继承(不共有域)
java
public class GetterCopyDemo {
private static final String TAG = GetterCopyDemo.class.getSimpleName();
public static class Node {
private final String name;
public Node(String name) {
this.name = name;
}
@NonNull
@Override
public String toString() {
return "Node{" + name + '}';
}
}
public static class BaseInfo {
private Node mNode;
public BaseInfo() {
}
// 拷贝构造:以后新增字段只改这里一处
public BaseInfo(BaseInfo other) {
this.mNode = other.mNode;
}
public Node getNode() {
return mNode;
}
public void setNode(Node node) {
this.mNode = node;
}
@NonNull
@Override
public String toString() {
return "BaseInfo{node=" + mNode + '}';
}
}
public static class ChildInfo extends BaseInfo {
private String mLabel; // child 自己的属性
public ChildInfo(BaseInfo origin) {
super(origin); // 一行复制 origin 的所有字段
}
public void setLabel(String label) {
this.mLabel = label;
}
@NonNull
@Override
public String toString() {
return "ChildInfo{" + super.toString() + ", label=" + mLabel + '}';
}
}
public static void demo() {
Log.d(TAG, "------------------------------------------------");
BaseInfo origin = new BaseInfo();
origin.setNode(new Node("node1"));
ChildInfo child = new ChildInfo(origin);
child.setLabel("我是child自己的属性");
ArrayList<BaseInfo> list = new ArrayList<>();
list.add(origin);
list.add(child);
for (BaseInfo baseInfo : list) {
Log.d(TAG, "info = " + baseInfo);
}
// child 是独立副本:改 origin 不会影响 child
origin.setNode(new Node("node2"));
Log.d(TAG, "--- after origin.setNode(node2) ---");
for (BaseInfo baseInfo : list) {
Log.d(TAG, "info = " + baseInfo);
}
}
}
对于如上代码,ChildInfo构造函数调用BaseInfo的拷贝构造,同时toString打印父类字段和子类字段,避免了上面的打印和实际值不一样的问题
info = BaseInfo{node=Node{node1}}
info = ChildInfo{BaseInfo{node=Node{node1}}, label=我是child自己的属性}
--- after origin.setNode(node2) ---
info = BaseInfo{node=Node{node2}}
info = ChildInfo{BaseInfo{node=Node{node1}}, label=我是child自己的属性}
origin.getNode().equals(child.getNode()) = false
组合(共有域)
public class GetterDelegateDemo {
private static final String TAG = GetterDelegateDemo.class.getSimpleName();
public static class Node {
private final String name;
public Node(String name) {
this.name = name;
}
@NonNull
@Override
public String toString() {
return "Node{" + name + '}';
}
}
public interface ViewInfo {
Node getNode();
}
public static class BaseInfo implements ViewInfo {
private Node mNode;
@Override
public Node getNode() {
return mNode;
}
public void setNode(Node node) {
this.mNode = node;
}
@NonNull
@Override
public String toString() {
return "BaseInfo{node=" + mNode + '}';
}
}
public static class ChildInfo implements ViewInfo {
private final ViewInfo origin; // 父类那部分委托 origin,自己不存
private String mLabel; // child 自己新增的属性,自己存
public ChildInfo(ViewInfo origin) {
this.origin = origin;
}
@Override
public Node getNode() {
return origin.getNode(); // 委托 origin
}
public String getLabel() {
return mLabel; // 自己的属性走自己的字段
}
public void setLabel(String label) {
this.mLabel = label;
}
@NonNull
@Override
public String toString() {
return "ChildInfo{origin=" + origin + ", label=" + mLabel + '}';
}
}
public static void demo() {
Log.d(TAG, "------------------------------------------------");
BaseInfo origin = new BaseInfo();
origin.setNode(new Node("node1"));
ChildInfo child = new ChildInfo(origin);
child.setLabel("我是child自己的属性");
ArrayList<ViewInfo> list = new ArrayList<>();
list.add(origin);
list.add(child);
for (ViewInfo baseInfo : list) {
Log.d(TAG, "info = " + baseInfo);
}
// child 是委托:改 origin 会影响 child
origin.setNode(new Node("node2"));
Log.d(TAG, "--- after origin.setNode(node2) ---");
for (ViewInfo baseInfo : list) {
Log.d(TAG, "info = " + baseInfo);
}
Log.d(TAG, "origin.getNode().equals(child.getNode()) = " + origin.getNode().equals(child.getNode()));
}
}
对于如上代码,ChildInfo持有BaseInfo,将父类字段给BaseInfo,并抽象ViewInfo用于遍历,同时toString打印父类字段和子类字段,避免了上面的打印和实际值不一样的问题
info = BaseInfo{node=Node{node1}}
info = ChildInfo{origin=BaseInfo{node=Node{node1}}, label=我是child自己的属性}
--- after origin.setNode(node2) ---
info = BaseInfo{node=Node{node2}}
info = ChildInfo{origin=BaseInfo{node=Node{node2}}, label=我是child自己的属性}
origin.getNode().equals(child.getNode()) = true