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
相关推荐
全栈陈序员2 分钟前
【Python】基础语法入门(二十九)—— 正则表达式实战:文本处理利器
开发语言·人工智能·python·正则表达式
Wang's Blog8 分钟前
Java 项目实战: 交友项目 - 发布评论
java·开发语言·交友
屈冠成9 分钟前
ShopXO商城系统测试报告
开发语言·spring·maven·lua
多弗朗皮卡丘16 分钟前
C++STL之空间配置器
开发语言·c++·内存池·空间配置器
tommyjiatong19 分钟前
什么是PHP漏洞
开发语言·php
CodeYp201928 分钟前
第19讲:封装红黑树实现 mymap 和 myset
开发语言·c++·红黑树
qq_3646821538 分钟前
录音APP怎么切换语言?多语言版本如何使用
java·服务器·前端
Wang's Blog42 分钟前
Java 项目实战: 交友项目 - 用户体系集成:根据环信id查询用户详情
java·lua·交友
美狐美颜SDK开放平台44 分钟前
视频美颜SDK如何应对快速转头与大幅度面部动作?
android·人工智能·ios·音视频·直播美颜sdk·视频美颜sdk
摇滚侠1 小时前
《On Java 中文版 基础卷》阅读笔记 复用 13
java·笔记·python