Java中级——组合和继承

组合和继承

这是两种不同的实现方式,不能混用

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
相关推荐
hai_android6 小时前
InputStage 责任链核心执行流程
android·android studio·android jetpack
Escalating_xu6 小时前
【C 语言】深入理解指针(5):sizeof、strlen、数组名语义与指针笔试题全解析
java·c语言·开发语言
HEJOO96 小时前
Linux 设备驱动系列——使用全局工作队列
java·linux·算法
谢亮_vipxieliang6 小时前
Go Channel 高级模式——从底层原理到扇出扇入实战
java·数据库·golang
Code_Solitude6 小时前
c语言:递归
java·c语言·算法
Nebula_g7 小时前
JavaSE加强:Stream流
java·开发语言·windows·stream·javase·流
估值探索者7 小时前
【Python量化系统工程实战 #08】从脚本到生产:量化系统上线 checklist 的最小闭环
java·开发语言·jvm·python·数据挖掘·数据·股票数据api接口
Wang's Blog7 小时前
Java框架 SpringCloud 快速入门: Nacos 环境隔离
java·开发语言·spring cloud
万年咸鱼7 小时前
解决C语言的内存释放机制
java·c语言·python
破芽7 小时前
python知识点整理02
开发语言·python